Three agents agreed on what a selectable product card should be
/ticket-storeCaptured 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
Three agents agreed on what a selectable product card should be
Build log series: I give an AI that has never seen Gunjo (
@gunjo/ui) the public npm package and the gunjo.jp docs, then ask it to build a real screen. This was the third deeper passenger-facing rail round, a discount ticket and day-pass purchase flow covering product comparison, selection, quantity, and payment.
Choosing a ticket product means choosing one priced card. The question for this round was whether the agent would hand-roll that pattern for the third time.
Result: 3.5/5, and the third RadioCard case fired
The purchase flow passed tsc and fit a 375px mobile-first layout. The agent reported:
AmountBreakdownwas the best match in the library. Adults times quantity plus children minus discounts became a ten-line data map ending in the amount due. NumberInput layout="stepper" explicitly names purchase quantity pickers, so it handled adult and child counts directly.Stepper,MetadataList, andBadgealso needed no hand-rolling. But choosing a product with a prominent price was the largest passenger-facing gap.
The core observation: the agent identified the accessibility model too
Product selection, a selectable card with a price, was hand-rolled and was the largest gap. There was no
RadioCardorSelectableCard. The by-use-case catalog and theListCarddocs pointed toListCard, but ListCardusesaria-pressed, the semantics of a toggle button, not a single-choice radio. Eightbutton[aria-pressed]options make a screen reader announce independent toggles instead of one of eight.RadioGrouphas the right semantics but only a 16px dot and text, not a card. "RadioCardis the semantics ofRadioGroupwearing the body ofListCard: a prominent price slot, a savings callout, a selection check, and a ring. It removes about 120 lines. This is the highest-value addition."
The same thing had happened three times in a row: limited-express tickets in #109, a commuter pass with priced duration cards in #119, and ticket products here. Three separate agents independently reached the same accessibility problem: an aria-pressed toggle is not a radio choice. I built the component immediately.
What I built: RadioCard
<RadioCardGroup value={value} onValueChange={setValue} aria-label="Discount tickets">
<RadioCard value="holiday" title="Holiday Outing Pass" description="Greater Tokyo area, weekends and holidays only"
tags={<Badge variant="success">For sightseeing</Badge>} price={formatCurrency(2720)} highlight="Save about ¥1,000" />
</RadioCardGroup>
RadioCardGroupprovidesrole="radiogroup", roving tabindex, and arrow-key movement, following the library's existing WAI-ARIA pattern forRadioGroup.RadioCardis arole="radio"card with tags, title, area or duration, a prominent price slot, a savings callout, selection check, ring, and a target of at least 44px.- Real single-choice radio semantics fixed the toggle problem in
ListCard. The catalog now distinguishes "choose one priced card" withRadioCardfrom navigation rows withListCard. - Issue #370 closed in PR#371. On a real 375px render, selection worked by click and ArrowDown, exactly one item always had
aria-checked, the target was 92px tall, nothing overflowed, and the console stayed clean.
What I built under src this round: RadioCard, on its third case and as the seventh component produced by the series. AmountBreakdown crossed an industry boundary for the eighth time. I also noted another hand-rolled category strip for FilterChips on issue #365, and the lack of a success state on StatusScreen, which could misrepresent purchase completion, as the second case on issue #366.
What I took away: three agents agreed on accessibility, not just appearance
RadioCard differed from the earlier components because three agents independently agreed not only on how it should look, but on why implementing a single choice with aria-pressed toggles was semantically wrong. The rule of three was not merely three reports of the same missing component. It was three independent paths to the same correct design. Building the result became transcription rather than speculation. Deeper passenger coverage had settled another building block of the purchase flow.
📊 Component scoreboard
| Built (7) | AmountBreakdown / ActionQueue / ListCard / Gantt-segments / SeatMap / LoyaltySummaryCard / RadioCard |
| In progress | FilterChips (category strip) two of three / TicketStub one of three / StatusScreen success two of three / Gantt time axis two of three |
Next up: build log #122
- Continue the passenger backlog with an international air e-ticket for the second
TicketStubcase, baggage tracking, or a terminal map for the thirdFilterChipscase. Rail alternatives are station recommendations and refunds.
The run
- gunjo.jp / RadioCard / AmountBreakdown / npm
@gunjo/ui/ GitHub / previous rounds #1 through #120 - GunjoUI by UIXHERO
A building block of passenger purchasing became a component on its third case, with three agents agreeing on the accessible model behind it.
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.