Round #121Score 3.5/5Transport: rail

Three agents agreed on what a selectable product card should be

Route: /ticket-store
Desktop preview
Mobile preview

Captured 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:

AmountBreakdown was 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, and Badge also 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 RadioCard or SelectableCard. The by-use-case catalog and the ListCard docs pointed to ListCard, but ListCard uses aria-pressed, the semantics of a toggle button, not a single-choice radio.⁠ Eight button[aria-pressed] options make a screen reader announce independent toggles instead of one of eight. RadioGroup has the right semantics but only a 16px dot and text, not a card. "RadioCard is the semantics of RadioGroup wearing the body of ListCard: 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 , a commuter pass with priced duration cards in , 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>
  • RadioCardGroup provides role="radiogroup", roving tabindex, and arrow-key movement, following the library's existing WAI-ARIA pattern for RadioGroup.
  • RadioCard is a role="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" with RadioCard from navigation rows with ListCard.
  • 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 progressFilterChips (category strip) two of three / TicketStub one of three / StatusScreen success two of three / Gantt time axis two of three

Next up: build log

  • Continue the passenger backlog with an international air e-ticket for the second TicketStub case, baggage tracking, or a terminal map for the third FilterChips case. Rail alternatives are station recommendations and refunds.

The run

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.

業界別「AI指示書パック」、先行登録を受付中です。