ListCard emerged after one pattern recurred across consumer screens
/service-statusCaptured at a 375px viewport. Long pages scroll inside the frame.
The screenshots stay in Japanese. Every round is a real screen for a real Japanese industry, so an English re-render would no longer be the screen the agent actually produced.
Write-up
ListCard emerged after one pattern recurred across consumer screens
Build log series: For each entry, I give an AI unfamiliar with Gunjo (
@gunjo/ui) the public npm package and the gunjo.jp docs, then ask for a working screen. The third passenger-facing rail round was a service-status page with line-by-line conditions, suspensions, and an update history, the kind of screen everyone checks when trains are delayed.
After limited-express booking in #109 and journey planning in #110, this was the third passenger-facing rail screen. The central question was whether result cards and status rows would be hand-rolled for a third time and trigger a real component.
Result: 3.5/5, and the first passenger component reached three cases
The line-status row became the third independent version of the same consumer pattern. The screen passed tsc and build, fit a mobile-first 375px layout, and produced no console errors across critical service notices, line status, line details, and update history. The agent reported:
(a) The line-by-line service-status row was hand-rolled. It is the center of the screen, and the library had nothing for it. The missing status result row or list card needs a leading color swatch, title, status pill, supporting line, update time, severity accent, and chevron, all in one tappable row. "This is the most reusable pattern in any consumer app that lists things with states, the library's largest consumer-facing gap, and the single most valuable addition." (b)
ActionQueuefit critical service notices perfectly. It was the one place where the library felt unmistakably purpose-built, and "the reason to keep using the library."
Three screens in a row produced the same result: train candidates in #109, journey candidates in #110, and line statuses in #111. Three separate agents independently hand-rolled the same tappable list row. The rule of three triggered. It was built for real immediately, becoming Gunjo's first consumer-facing component from this run.
Built for real: ListCard
<ListCard
leading={<span className="size-3 rounded-full bg-destructive" />}
title="Chuo Line Rapid" description="Injury on the line"
status={<Badge variant="destructive">Service suspended</Badge>}
meta="Updated 7:42" severity="critical"
onSelect={() => openLine("chuo")}
/>
- Leading content, title, supporting text, tag, status, metadata, chevron, a left severity accent, and selected state.
- With
onSelect, the whole card is a real button at least 44px high, addressing the test finding about undersized touch targets, with hover, focus, andaria-pressedbehavior. - Status does not rely on color alone, combining an icon-bearing
Badgewith the severity accent. Server rendering is safe by default. - The same component serves search results and service-status rows as the component for lists of consumer-facing objects.
- Closed issue #135 with PR#359. On a 375px device, touch targets measured 58 to 73px, selection moved on tap, and the console stayed clear.
I also fixed false leads for status rows
This round, the table sent "status row" to PersonCell for people and "line information" to RouteStops for delivery stops. I renamed the group "lists, results, and tables," added ListCard, and stated that this use case is not PersonCell for people, RouteStops for delivery stops, or DataTable for administrative tables. That corrected the false leads from #109, #110, and #111 together.
What was added under src this round: ListCard, on its third case. ActionQueue was independently rediscovered for a fourth consecutive rail screen, this time for triaging critical service information.
What I took away: consumers have a cross-industry foundation too
On staff-facing screens, the rule of three established a shared foundation in StatGroup for metrics, AmountBreakdown for money, and ActionQueue for items requiring action. The same process worked for consumers. ListCard, a list of objects, carries across search results, product comparisons, status lists, order rows, and device lists. It is the backbone of consumer apps. Three passenger screens were enough to add that backbone.
The table's accumulated improvements continued too. Since creating it in #100, I have added entries, documented unsuitable contexts, and corrected false leads on every round. This time I removed the status-row misdirection and pointed the table to ListCard. A component fills the gap, and the table points to it: that loop now works for consumers as well as staff. The remaining mode-specific consumer foundations are SeatMap (issue #356, one of three) and TransitItinerary (issue #358, one of three).
Next up: build log #112
- Three passenger-facing rail screens complete this set. Next I may try passenger air booking for a second
SeatMapcase, staff or passenger screens for bus, taxi, or trucking, or one more rail screen such as station navigation. I will also watch whether an agent in another industry findsListCardindependently.
The run
- gunjo.jp / ListCard / ActionQueue / the by-use-case table /docs/by-use-case / npm
@gunjo/ui/ GitHub / previous rounds #1 through #110 - GunjoUI by UIXHERO
The third hand-built consumer list became ListCard immediately.
This series is co-created with AI (Claude and Codex). A human designs the experiments, makes the calls, and fact-checks before publishing; the AI does the hands-on work and drafting.
Components from @gunjo/ui
The components this screen's source imports directly.
The code the agent actually wrote
Click a file to expand its source.