The third hand-rolled seat map is the one that became a component
/flight-checkinCaptured 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
The third hand-rolled seat map is the one that became a component
Build log series: An AI that has never seen Gunjo (
@gunjo/ui) gets the public npm package and the gunjo.jp docs, then builds a real screen. This was the first of three passenger-facing air rounds run together, an online check-in flow covering flight confirmation, seat changes, baggage, and the boarding pass.
Passenger-facing screens are the weak side of the library, so I decided to go deeper there and ran three air screens (#114 through #116) in parallel. This is the first. What I was watching: would the seat map be hand-rolled a third time?
Result: 3/5, and the third SeatMap case fired
The screen passed tsc, fit a 375px mobile-first layout, produced no console errors, and covered four steps: confirm, seat, baggage, boarding pass. The agent reported:
The seat map was hand-rolled from scratch, and it is the leading gap. The nearest thing,
ScheduleGridfor staff availability across days, has no aisle gap and no type or fee per seat. "ASeatMapcomponent needs configurable aisle positions,{id, state, type, fee}per cell, controlledselectedandonSelect, states that do not rely on color alone, a legend with fees, and keyboard movement across the grid. The library has nothing anywhere near this."
That makes three rounds running. Rail seats in #109, cabin seats in #112, and the seat change during check-in here. Three separate agents independently hand-rolled a seat map of about 190 lines each. The rule of three fired, so I built it.
What I built: SeatMap
const columns = ["A","B","C", null, "D","E","F"] // null = aisle (a 3-3 cabin)
<SeatMap columns={columns} seats={seats}
selectedIds={selected} maxSelectable={2} onToggle={toggle} />
- Aisle gaps as null columns, covering 3-3, 2-4-2, and 2-2 layouts; a state, type, position, and fee per seat; controlled multi-select with a capacity cap, where reaching the cap disables the seats not yet chosen.
- Grid accessibility:
role=grid, arrow keys to move focus,aria-selectedandaria-disabled, and a composed accessible name ("seat 12A, window, exit row, available, 1,500 yen"). State never depends on color alone: a check for selected, a cross for booked, and screen-reader-only labels. - It contains its own horizontal scrolling, so a 3-3 cabin does not break the page at 375px.
- That closes issue #356 in PR#361, verified on a real 375px render with 36 seats, 34 of them disabled once two were selected and the cap was reached, composed names carrying type, fee, and position, and no errors. I also corrected the table's seat-selection lead to
HeatmapChart, which is now documented as read-only.
Logged, not built
- 🟢
TicketStuborPassCard for a boarding pass: origin and destination, times, a prominent gate, a perforation, and a barcode area.SignedRecordis an audit record, not a pass. Recorded on issue #363 at one of three. - 🟢
StatusScreenhas no success variant even though the table sends confirmation and completion screens to it, so a "boarding complete" message could arrive in error styling. Recorded on issue #366. - 🟢 44px touch targets.
Buttondefaults to 36px and itsxlsize does reach 44px, but the agent never found it. Recorded on issue #362, now seen three times across #114, #115, and #116.
What I built under src this round: SeatMap, on its third case. AmountBreakdown crossed an industry boundary again, this time for baggage fees.
What I took away: mode-specific passenger components settle under the rule of three too
SeatMap is a passenger-facing foundation specific to one mode. It reached its third case across rail seats, cabin seats, and a seat change during check-in, and settled. That contrasts with ListCard, a shared foundation that paid off across industries at once: SeatMap had to collect its evidence across modes and flows before it could be built. Going deeper on the passenger side fills both kinds under the same rule. The weak side of the library is where the next foundation shows up.
Next up
- The other two rounds in this passenger-facing air batch: the flight status board in #115 and booking management with mileage in #116.
The run
- gunjo.jp / SeatMap / the by-use-case table /docs/by-use-case / npm
@gunjo/ui/ GitHub / previous rounds #1 through #113 - GunjoUI by UIXHERO
A seat map hand-rolled on three screens became a component the same day.
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.