The AI Playbook for Design Systems: What Works, What Fails, and Why
How do ChatGPT o3, Claude 3.5 Sonnet, and DeepSeek R1 compare when planning enterprise-strength design system components? The results shocked me!
A Refresher On Code Mockups
Last week, I introduced code mockups—a structured approach to designing UI components before they’re built. This method improves scalability and maintainability, but it’s time-consuming, requiring deep exploration, iteration, and validation.
AI has the potential to change that. By automating pattern exploration, generating usage examples, and enforcing best practices, Engineers can iterate on AI-assisted scaffolding—focusing on high-impact decisions instead of repetitive setup.
In this article, I’ll explore how AI enhanced the code mockup workflow, making it faster, more efficient, and accessible. Plus, I’ll share how we’re applying it at VividSeats to streamline design system development.
Let’s dive in.
What Are We Working With?
1. The Workflow
Copy everything from Code Mockups For Enterprise-Strength Design System Components to the end and paste it in The Prompt, replacing {{WORKFLOW}}
. Remember to strip out any promotional sections.
2. The Template
Copy everything in the code block below and paste it in The Prompt, replacing {{TEMPLATE}}
:
# Code Mockup: <Component /> [Template]
**Category:**
**Pattern:**
Choose One:
- Standalone Component
- Compound Component
- Base Component
- Compound-as-Base-Hybrid
**Key Stakeholders:**
- Recommend: _Your Name_
- Sponsor: _Design System Maintainer Name_
- Implement: _The Engineer doing the work if known_
> **High-level Details**
> _The code below is meant to give an idea of what the implementation in
> Next might look like from a birds-eye view. It is not meant to (and
> will not) be 100% correct; actual implementation details may vary._
## Pattern Notes
> **DELETE BEFORE REVIEW**
> _Add any details to this section that explain alterations of the
> pattern or ways we might expect the pattern to evolve or change as
> this component scales in complexity. Remove the entire section if you
> have no notes prior to review._
## Variants
> **DELETE BEFORE REVIEW**
> _Delete this entire section if this is not a **Compound Component** or
> **Compound-as-Base-Hybrid**. Otherwise, rename the below keeping the
> naming convention for **Base Components** using the name of the base
> as the suffix in variant names (e.g. variants of a base component
> `Button` should be named `ThisButton`, `ThatButton`, etc.)_
Component will be used as a base in the following shared components:
- `VariantComponent`
- `VariantComponent`
- `VariantComponent`
- `VariantComponent`
## Composition Strategy
> **DELETE BEFORE REVIEW**
> _Add mocked up component usage code snippets in this section._
### Approach #1:
### Approach #2:
## Props
### Component
> **DELETE BEFORE REVIEW**
> _Add to the following set of minimum required props. Check our prop
> standards to ensure consistency in our component contracts.
> Review guidelines to ensure props avoid becoming interdependent.
> Leave comments or footnotes exploring alternative approaches for
> specific prop(s) as needed._
| PROP | TYPE | USAGE |
| --------- | ------------------------------------------- | -------- |
| as | `keyof JSX.IntrinsicElements | ElementType` | OPTIONAL |
| className | `string` | OPTIONAL |
| error | `Error | null` | OPTIONAL |
| loading | `boolean` | OPTIONAL |
| testId | `string` | OPTIONAL |
### Component.SubComponent
> **DELETE BEFORE REVIEW**
> _Add to the following set of minimum required props. Check our prop
> standards to ensure consistency in our component contracts.
> Review guidelines to ensure props avoid becoming interdependent.
> Leave comments or footnotes exploring alternative approaches for
> specific prop(s) as needed._
| PROP | TYPE | USAGE |
| --------- | ------------------------------------------- | -------- |
| as | `keyof JSX.IntrinsicElements | ElementType` | OPTIONAL |
| className | `string` | OPTIONAL |
| error | `Error | null` | OPTIONAL |
| loading | `boolean` | OPTIONAL |
| testId | `string` | OPTIONAL |
> **DELETE BEFORE REVIEW**
> _Add one of these sections for each subcomponent if the component is a
> **Compound Component** or **Compound-As-Base-Hybrid**. Remove this
> entire section if the component is a **Standard Component** or **Base
> Component**._
> **DELETE BEFORE REVIEW**
> _The following section(s) are optional but recommended so that we
> don’t keep reinventing wheels._
## Utils
### Hooks
### Functions
### Types & Interfaces
### Enums & Constants
3. The Prompt
[text surrounded by brackets]
is optional.{{TEXT}}
using handlebars syntax indicates where specific content should be injected.(pick one: foo|bar|baz)
indicates where you should pick one of the provided options.
Write a Code Mockup for a new <Component /> component[ implemented as a (pick: standard|compound|base) component].
Here's the workflow for writing a Code Mockup:
"{{WORKFLOW}}"
Here is the list of all Standardized Props for context:
| PROP | TYPE | USAGE |
| --------------- | -------------------------------------------- | -------- |
| as | `keyof JSX.IntrinsicElements \| ElementType` | OPTIONAL |
| animated | `boolean` | OPTIONAL |
| className | `string` | OPTIONAL |
| children | ReactNode | OPTIONAL |
| disabled | `boolean` | OPTIONAL |
| error | `Error \| null` | OPTIONAL |
| fullWidth | `boolean` | OPTIONAL |
| loading | `boolean` | OPTIONAL |
| testId | `string` | REQUIRED |
| onPress() | `PressEventHandler` | OPTIONAL |
| onPressEnter() | `EnterEventHandler` | OPTIONAL |
| onPressEscape() | `EscapeEventHandler` | OPTIONAL |
| onPressSpace() | `SpaceEventHandler` | OPTIONAL |
| onPressTab() | `TabEventHandler` | OPTIONAL |
| onTabBack() | `TabBackEventHandler` | OPTIONAL |
Use this template for the Code Mockup:
```Markdown
{{TEMPLATE}}
```
[Optionally, add a short description of the differing approaches you're looking into in your mockups. Design Responsibility? Splitting up components? Any other details that are relevant?]
The response may be output as regular text; markdown from the template may be applied instead of preserved. Do not complete the Key Stakeholders section, as it will be filled out later. Remember that code mockups for design system components are about abstracting the underlying mechanics, so do not generate any code which includes implementation details of this component or its potential ancillary dependencies. Utility functions, hooks, enums, constants, etc should be described in juxtaposition to each other with regard to potential approaches. Prop and ancillary types and interfaces are an exception to this rule, since they are separate from implementation. Remember when naming types and interfaces that they should not know they are types and interfaces respectively, so do not prefix names with `T` or `I`, nor include any other such redundancy like ending the name with `Type`, or `Data` for types/interfaces that represent data. Generics and type parameters should use capitalized single letter names from the letter `T`, incrementing one letter of the alphabet for each additional generic or type parameter. Arrays should use generic syntax (i.e. `Array<T>` instead of `T[]`.) Do not use object literal types unless writing a mapped type, since index mapping and key remapping are not allowed in interfaces. Properties in interfaces should be `readonly` except in cases where they are intentionally mutable; likewise, arrays should be typed as `ReadonlyArray`s unless they are intentionally mutated in place. Destructure type and value imports from React rather than using the `React` namespace with a default import named React. Do not use "style" props like `marginTop` or `border` unless they are already included on the list of Standardized Props. Keep usage snippets as focused as possible, and do not include surrounding boilerplate unless absolutely necessary.
The Results
I trialed and refined this process with multiple models to see which provides the best jumping-off point, and the results were not what I was expecting.
I wanted to see how AI would handle heterogenous levels of complexity, so I experimented with the following components:
<Kbd />
An atomic component so simple one might wonder why it exists at all when one could just use the<kbd>
HTML element directly.<Breadcrumbs />
A molecule component just complex enough that it can be implemented with any of the three main patterns described in The Workflow.<Table />
An organism component too complex to be anything other than a compound component.
The only prompt which specified the pattern was the prompt for the Table component. Other than this, I was hands off and did not add any context or details about how the component could or should be built.
ChatGPT o3
<Kbd />
- ⚠️ Questionable
ChatGPT o3
Pattern: Standalone component
Approach #1: Basic Usage
<Kbd testId="kbd-basic">Enter</Kbd>
Approach #2: Composite Key Sequence
<Kbd className="custom-kbd" testId="kbd-sequence">
<span>Ctrl</span> + <span>Shift</span> + <span>Esc</span>
</Kbd>
No discrete props
No ancillary resources
Review
Approach #1 closely follows the behavior of the <kbd>
HTML element, making it a familiar and intuitive choice.
Approach #2 is interesting because it supports multiple keys, though the usage snippet demonstrates bad technique by wrapping keys in spans.
It also looks like the AI misunderstood the testId prop being marked as REQUIRED in the Standardized Props table. This designation simply means the component must support testId
, not that every instance must include it. In general, props—especially standardized ones—should rarely be mandatory unless there's a compelling reason to enforce their usage.
<Breadcrumbs />
- ❌ Not Passable
ChatGPT o3
Pattern: Compound component
Subcomponents:
Breadcrumbs.Item
Approach #1: (the AI did not name either approach)
<Breadcrumbs testId="breadcrumbs">
<Breadcrumbs.Item as="a" href="/" testId="breadcrumb-home">
Home
</Breadcrumbs.Item>
<Breadcrumbs.Item as="a" href="/products" testId="breadcrumb-products">
Products
</Breadcrumbs.Item>
<Breadcrumbs.Item testId="breadcrumb-current">
Current Page
</Breadcrumbs.Item>
</Breadcrumbs>
Approach #2:
const breadcrumbItems = [
{ label: 'Home', href: '/', testId: 'breadcrumb-home' },
{ label: 'Products', href: '/products', testId: 'breadcrumb-products' },
{ label: 'Current Page', testId: 'breadcrumb-current' },
];
<Breadcrumbs testId="breadcrumbs" className="custom-breadcrumbs">
{breadcrumbItems.map((item, index) => (
<Breadcrumbs.Item
key={index}
as={item.href ? 'a' : 'span'}
href={item.href}
testId={item.testId}
disabled={item.disabled}
>
{item.label}
</Breadcrumbs.Item>
))}
</Breadcrumbs>
No discrete props
Ancillary Resources:
Hooks:
useBreadcrumbsContext()
and an optionaluseResponsiveBreadcrumbs()
hook to handle dynamic layout adjustmentsFunctions:
formatBreadcrumbs()
Types:
BreadcrumbsProps
,BreadcrumbItemProps
Constants:
BREADCRUMBS_SEPARATOR
Review
This fails because it’s essentially the same approach served two different ways.
The subcomponent naming is lacking in refinement—Breadcrumbs.Item
feels okay but a bit generic and uninspired. A more semantic choice like Breadcrumbs.Crumb
or a more streamlined option like Breadcrumbs.Link
would improve clarity and usability.
Then there’s formatBreadcrumbs()
—which, as far as I can tell, isn’t even being used. The output describes it as a function that converts data into an array of Breadcrumbs.Item
subcomponents, but the usage snippets either handle this conversion manually or skip it altogether by passing subcomponents as children. Even if it were used, it should probably be a custom hook since it’s returning presentation logic, not just transformed data.
<Table />
- ❌ Not Passable
ChatGPT o3
Pattern: Compound component (the output implies more of a Compound-as-Base Hybrid by including specialized variants)
Variants:
DataTable
,SortableTable
,PaginatedTable
,ResponsiveTable
Subcomponents:
Table.Header
,Table.Row
,Table.Cell
,Table.Body
,Table.Footer
,Table.Caption
Approach #1: Basic Table Structure
<Table testId="basic-table" className="custom-table">
<Table.Header>
<Table.Row>
<Table.Cell as="th">Name</Table.Cell>
<Table.Cell as="th">Age</Table.Cell>
<Table.Cell as="th">Location</Table.Cell>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row>
<Table.Cell>Jane Doe</Table.Cell>
<Table.Cell>29</Table.Cell>
<Table.Cell>New York</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>John Smith</Table.Cell>
<Table.Cell>34</Table.Cell>
<Table.Cell>San Francisco</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
Approach #2: Table with Footer and Optional Caption
I decided to omit the code snippet for this to save space since its the same as the above but with:
<Table.Caption>Employee Directory</Table.Caption>
No discrete props
Ancillary Resources:
Hooks:
useTableSort()
and an optionaluseTablePagination()
hookFunctions:
calculateColumnWidths()
,mergeCellStyles()
Types: Prop types for each subcomponent
Enums:
TableSortDirection
(e.g.ASC
andDESC
)Constants:
DEFAULT_CELL_PADDING
Review
This fails pretty much right off the bat because the usage snippets show Table.Header
and Table.Footer
in both approaches but the title of Approach #2 implies that basic table structure should not include a header and footer.
Also, only standardized props? No discrete props? Not even a sortOrder
prop that uses a TableSortDirection
?
Claude 3.5 Sonnet
<Kbd />
- ✅ Passable Rough Draft
Claude 3.5 Sonnet
Pattern: Standalone or Compound Component
Approach #1: Single Component with Key Combinations
// Individual key
<Kbd>⌘</Kbd>
// Key combination
<Kbd>⌘ + K</Kbd>
// Sequential combination
<Kbd>g g</Kbd>
// With custom separator
<Kbd separator="+">⌘ K</Kbd>
// Nested in text
Press <Kbd>⌘</Kbd> + <Kbd>K</Kbd> to open the command palette
Approach #2: Compound Component with Specialized Parts
Subcomponents:
Kbd.Key
,Kbd.Group
, Kbd.Separator
// Individual key
<Kbd.Key>⌘</Kbd.Key>
// Key combination
<Kbd.Group>
<Kbd.Key>⌘</Kbd.Key>
<Kbd.Separator>+</Kbd.Separator>
<Kbd.Key>K</Kbd.Key>
</Kbd.Group>
// Sequential combination
<Kbd.Group sequential>
<Kbd.Key>g</Kbd.Key>
<Kbd.Key>g</Kbd.Key>
</Kbd.Group>
Discrete Props:
Kbd:
separator
,size
,variant
Kbd.Group:
sequential
Ancillary Resources:
Types: Prop types for the component in Approach #1 and for each subcomponent in Approach #2 except for Kbd.
Separator
Enums:
KbdSize
,KbdVariant
Constants:
DEFAULT_KBD_SEPARATOR
Review
The difference in quality between this and ChatGPT o3 is Grand Canyon huge. This is mind-blowingly better. Multiple patterns, discrete props, utils, the works! On a component this simple, output this good can be used with only minor refinement.
If an Engineer at Vivid Seats brought this forward for review with the VSDS team, my main feedback would be that Kbd.Key
should just be the parent Kbd
so we don’t bloat complexity with redundant subcomponents. While compound components typically take multiple subcomponents as children, in some cases they can use an inverted form which allows for a subcomponent (typically Group
) to take the parent as children instead.
I would also dig in a bit to understand why including variants is necessary to meet requirements for this component. It’s not out of the realm of possibility, but without a mockup backing it up it feels over-Engineered. If we actually have a business case for KbdVariants
, then naturally a question of whether we should consider a Base Component pattern for these variants would follow.
Also, the Kbd.Separator
subcomponent is unnecessary. Given the way keyboard commands chain together, we have no reason not to assume that “+” should be the operator until or unless business requirements make it clear otherwise. That said, if we kept this subcomponent, it should use a string-only semantic as
prop instead of accepting a text child each time so that we can rely on DEFAULT_KBD_SEPARATOR
unless we need to override it.
<Kbd.Separator as="+" />
Lastly, size
should be fontSize
, if we include a specialized prop for this at all. As an inline element, it’s size should scale conformally with the size of the text it contains, so there’s no need for an enum like KbdSize
.
I’d probably still lean towards Approach #1, but this at least gave us an idea of what another possibility might look like, even if just to confirm it’s too much. All in all, a very impressive jumping-off point.
<Breadcrumbs />
- ✅ Passable Rough Draft
Claude 3.5 Sonnet
Pattern: Standalone or Compound Component
Approach #1: Data-Driven Composition
<Breadcrumbs
items={[
{ label: "Dashboard", href: "/dashboard" },
{ label: "Projects", href: "/dashboard/projects" },
{ label: "Project Details" }
]}
/>
Approach #2: Explicit Composition
Subcomponents:
Breadcrumbs.Item
,Breadcrumbs.Separator
<Breadcrumbs>
<Breadcrumbs.Item>
<Link href="/dashboard">Dashboard</Link>
</Breadcrumbs.Item>
<Breadcrumbs.Separator>/</Breadcrumbs.Separator>
<Breadcrumbs.Item>
<Link href="/dashboard/projects">Projects</Link>
</Breadcrumbs.Item>
<Breadcrumbs.Separator>/</Breadcrumbs.Separator>
<Breadcrumbs.Item aria-current="page">
Project Details
</Breadcrumbs.Item>
</Breadcrumbs>
Discrete Props:
Breadcrumbs:
separator
Breadcrumbs.Item:
current
Ancillary Resources:
Hooks:
useBreadcrumbs()
vsuseRouteAsBreadcrumbs(
)Functions:
createBreadcrumbsFromPath()
vscreateBreadcrumbsFromRoutes()
Types:
BreadcrumbItem
,BreadcrumbContext
Review
Some criticism from the Kbd
component carry over, like Breadcrumbs.Separator
using children instead of a prop as needed. But the output is, again, miles better than ChatGPT o3.
The best thing about this output is that it includes Link
, showing how we can use other components compositionally. It also clearly juxtaposes two approaches for hooks and reflects the potential downstream impact of these approaches in subsequent utility functions.
Claude 3.5 Sonnet also used the Pattern Notes section to outline four ways we might want to change or improve the component in the future.
I would lean towards Approach #2 and give it a semantic as
prop so that we can streamline the usage like this:
<Breadcrumbs>
<Breadcrumbs.Item as={Link} href="/dashboard">
Dashboard
</Breadcrumbs.Item>
<Breadcrumbs.Separator />
<Breadcrumbs.Item as={Link} href="/dashboard/projects">
Projects
</Breadcrumbs.Item>
<Breadcrumbs.Separator />
<Breadcrumbs.Item aria-current="page">
Project Details
</Breadcrumbs.Item>
</Breadcrumbs>
<Table />
- ⚠️ Questionable
Claude 3.5 Sonnet
Pattern: Compound Component
Subcomponents: Table.Header, Table.Body, Table.Row, Table.Body, Table.Pagination
Approach #1: Explicit Structure
<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell sortable>Name</Table.HeaderCell>
<Table.HeaderCell>Email</Table.HeaderCell>
<Table.HeaderCell align="right">Status</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row>
<Table.Cell>Jane Cooper</Table.Cell>
<Table.Cell>jane@example.com</Table.Cell>
<Table.Cell align="right">Active</Table.Cell>
</Table.Row>
</Table.Body>
<Table.Footer>
<Table.Pagination />
</Table.Footer>
</Table>
Approach #2: Data-Driven
<Table
data={users}
columns={[
{ key: 'name', header: 'Name', sortable: true },
{ key: 'email', header: 'Email' },
{ key: 'status', header: 'Status', align: 'right' }
]}
>
<Table.Header />
<Table.Body>
{(row) => (
<Table.Row key={row.id}>
<Table.Cell>{row.name}</Table.Cell>
<Table.Cell>{row.email}</Table.Cell>
<Table.Cell align="right">{row.status}</Table.Cell>
</Table.Row>
)}
</Table.Body>
<Table.Pagination />
</Table>
Discrete Props:
Table:
data
,columns
,sortable
Table.Header:
sticky
Table.HeaderCell:
align
,sortable
,sortDirection
,onSort
Table.Row:
selected
Table.Pagination:
page
,pageSize
,onPageChange()
Ancillary Resources:
Events:
SortEvent
,SortEventHandler
,PageChangeEvent
,PageChangeEventHandler
Hooks: useTableSort(), useTablePagination(), useTableSelection(), useTableFilters()
Types:
Column
,TableData
,SortState
,Alignment
,SortDirection
,RenderFunction<T>
Review
The output for this one reaches the limits I think for what AI will be able to do here on its own, at least for now.
These two approaches aren’t really mutually exclusive, you’ll probably end up doing both depending on how the component is used throughout the app. Better approaches would be to compare heterogeneous levels of design responsibility, or alternatively juxtaposing a basic compound component with no pagination, sorting, filtering, etc against a more complex version with this more advanced functionality to get a sense of how the component might scale or be built iteratively.
And this is why Tables are hard. At its core, the Table
component operates as an ordered functional pipeline, where raw data flows through a series of pure transformations in a specific order before rendering. Each transformation is a function that takes the current dataset and returns a new, modified version:
paginate(sort(filter(search(data))))
We see a hook for filtering, but only sorting seems to be really thought out.
The best part of this output is that there are custom events! Custom events are also hard because they require special handling, don’t propagate predictably, and break framework optimizations. Instead of subclassing Event
or using CustomEvent
, extend native event types by defining subtypes in TypeScript for safer, more predictable behavior. Since we know that SortEvent
and PageChangeEvent
would be dispatched by pressing a button, we should type them like this:
type SortEvent<T> = PointerEvent<T>
type SortEventHandler<T> = PointerEventHandler<T>
type PageChangeEvent<T> = PointerEvent<T>
type PageChangeEventHandler<T> = PointerEventHandler<T>
In VSDS, we have a custom PressEvent
to make it easier to share code between Mobile and Web, so we would create subtypes of that instead. The output, however, included only basic interfaces for these events.
interface SortEvent {
readonly column: string
readonly direction: SortDirection
}
interface PageChangeEvent {
readonly page: number
readonly pageSize: number
}
This might be a quirk we can iron out with more explicit direction in the prompt, but for now it— combined with the rudimentary approaches are enough to put this one in the Questionable category, but not at all a bad start!
DeepSeek R1
<Kbd />
- ⚠️ Questionable
DeepSeek R1
Pattern: Standalone Component
Approach #1: Basic Semantic Usage
// Single key
<Kbd testId="enter-key">Enter</Kbd>
// Disabled state
<Kbd disabled testId="disabled-key">⌘</Kbd>
// Custom element with animation
<Kbd as="span" animated testId="animated-key">Tab</Kbd>
Approach #2: Keyboard Shortcut Grouping
// Composition with other elements
<div className="shortcut-group">
<Kbd testId="mod1">Ctrl</Kbd> +
<Kbd testId="mod2">Alt</Kbd> +
<Kbd testId="main-key">Del</Kbd>
</div>
No Discrete Props
Ancillary Resources:
Types:
StandardKbdProps
Review
This output feels a lot closer to ChatGPT o3. It doesn’t actually give us two different approaches, just two different use-cases. Most interestingly though is that, according to DeepSeek’s exposed inner monologue, it briefly considered a compound component before deciding not to explore that pattern. This makes me think that with a bit of massaging, the prompt could be Engineered to yield more exploratory results.
We don’t really need more than one pattern here, so this output is Questionable at best.
<Breadcrumbs />
- ✅ Passable Rough Draft
DeepSeek R1
Pattern: Compound Component
Approach #1: Explicit Composition
Subcomponents:
Breadcrumbs.Item
,Breadcrumbs.Separator
<Breadcrumbs testId="nav-trail">
<Breadcrumbs.Item href="/">Home</Breadcrumbs.Item>
<Breadcrumbs.Separator />
<Breadcrumbs.Item href="/products">Products</Breadcrumbs.Item>
<Breadcrumbs.Separator>
<IconChevronRight size={12} />
</Breadcrumbs.Separator>
<Breadcrumbs.Item isCurrent>Winter Collection</Breadcrumbs.Item>
</Breadcrumbs>
Approach #2: Automated Separation
<Breadcrumbs
testId="auto-trail"
separator={<IconSlash size={10} />}
items={[
{ href: '/', content: 'Home' },
{ href: '/settings', content: 'Settings' },
{ content: 'Profile' }
]}
/>
Discrete Props:
Breadcrumbs:
items
,separator
Breadcrumbs.Item:
isCurrent
Ancillary Resources:
Hooks:
useBreadcrumbItems()
Types:
BreadcrumbItem
,BreadcrumbSeparatorProps
Constants:
BREADCRUMB_ARIA_LABEL
,CURRENT_PAGE_ARIA_LABEL
Review
This output is really interesting because it felt Questionable at first but, after thinking over it a bit, I’ve realized that this Passable and actually slightly outperformed Claude 3.5 Sonnet.
Approach #2 is a bit worse than its Claude counterpart. I mean really, an IconSlash
component? But this does at least give a sense of how things could be handled compositionally, and its easy enough to revise this part to separator=”/”
.
Approach #1 is a bit better than its Claude counterpart because Breadcrumbs.Separator
is used correctly as a self-closing childless element.
<Table />
- ✅ Passable Rough Draft
DeepSeek R1
Pattern: Compound Component
Subcomponents:
Table.Header
,Table.Head
,Table.Body
,Table.Row
,Table.Cell
Approach #1: Basic Composition
<Table testId="user-table" fullWidth>
<Table.Header>
<Table.Head>Name</Table.Head>
<Table.Head align="end">Age</Table.Head>
</Table.Header>
<Table.Body>
<Table.Row>
<Table.Cell>John Doe</Table.Cell>
<Table.Cell align="end">30</Table.Cell>
</Table.Row>
<Table.Row disabled>
<Table.Cell>Jane Smith</Table.Cell>
<Table.Cell align="end" loading>{/* Shows skeleton */}</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
Approach #2: Enhanced Features
<Table testId="sortable-table" error={fetchError}>
<Table.Header>
<Table.Head
sortable
sortDirection="asc"
onSort={handleSort}
>
Name
</Table.Head>
<Table.Head
align="end"
sortable
sortDirection="desc"
onSort={handleAgeSort}
>
Age
</Table.Head>
</Table.Header>
{!fetchError && (
<Table.Body loading={isLoading}>
{/* Skeleton rows when loading */}
{data.map((item) => (
<Table.Row key={item.id}>
<Table.Cell>{item.name}</Table.Cell>
<Table.Cell align="end">{item.age}</Table.Cell>
</Table.Row>
))}
</Table.Body>
)}
</Table>
Discrete Props:
Table.Head:
align
,sortable
,sortDirection
,onSort
Table.Cell:
align
,colSpan
Ancillary Resources:
Events:
SortEvent
,SortEventHandler
Types:
Alignment
,SortDirection
Constants: ALIGN_CLASSES, SORT_ICON_DIRECTION
Review
This output is wild! DeepSeek R1’s MoE reasoning shines through here. Though not as sophisticated as a code mockup for a Table
component realistically would be, which may also touch on heterogenous levels of design responsibility, this output at least considers how our Table might impact the wider codebase as more is added to it.
And the code just feels cleaner than what we got out of ChatGPT o3 or Claude 3.5 Sonnet. That said, though, this output does have the same issues with custom events as Claude 3.5 Sonnet, so I’m pretty sure we need to be more explicit about expectations for custom events in the prompt.
Key Takeaways
This experiment showed that AI can accelerate UI code mockups, but human oversight is still essential. More importantly, I assumed that AI would get worse as components got more complex, but this did not turn out to be the case. In fact:
🔹 DeepSeek R1 is best for more complex components – Claude 3.5 Sonnet and ChatGPT o3 both struggled proportionally as components grew in complexity.
🔹 Claude 3.5 Sonnet is best for everything else – ChatGPT o3 lagged behind in generating substantive approaches, but Claude handled atomic and molecule-level components with ease.
AI isn’t ready to take on everything from planning to execution, but it can speed up ideation and exploration. The future of AI-assisted UI development isn’t just about velocity—it’s about leveraging AI to achieve new professional levels of craftsmanship without hitting any speed bumps.