SeatMap crossed into bus, but its attributes still spoke airline
/highway-busCaptured 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
SeatMap crossed into bus, but its attributes still spoke airline
Build log series: I give an AI that has never seen Gunjo (
@gunjo/ui) only the public npm package and the gunjo.jp docs, then ask it to build a real screen. This second passenger-facing bus round covered intercity bus seat booking from Shinjuku to Osaka: a three-across independent-seat layout, seat selection, fares, booking, and a boarding pass.
Result: 4/5. Five booking foundations crossed into intercity bus
The five-step flow passed tsc and fit a 375px mobile-first layout. The agent reported:
Five of the six leading pieces (
RadioCard,AmountBreakdown,Stepper,TicketStub, andPageHeader) introduced no friction and clearly target passengers in Japanese transportation. The by-use-case catalog led directly to the right component for all six pieces, with no false leads.
The booking foundation settled in rail and air required almost no hand-rolling for intercity bus: RadioCard for service plans, AmountBreakdown for fares, Stepper for booking, TicketStub for the QR pass, and PageHeader for the app bar.
The core observation: SeatMap had a general grid and airline-specific attributes
At the center of the screen, the agent found:
The grid worked perfectly.
columns={["A",null,"C",null,"D"]}represented three independent seats as one, aisle, one, aisle, one, while["A","B",null,"C","D"]represented a four-across layout. State,maxSelectable, window or aisleposition,fee, and grid accessibility were all correct. But bus-specific seat attributes did not fit the model.Seat.typeaccepts one string, while a bus seat can be women-only, near the restroom, have a power outlet, and sit in the front row at the same time. Combining those into one type forced the agent to split them back into a custom legend, and every type collapsed to the same info dot. The built-in legend used airline terms such as exit row and extra legroom and could not be relabeled, so the agent hand-rolled another legend. There was also no vehicle orientation or front slot, so it built a direction marker. The current component is an airline seat map that barely survived being pointed at a bus.
I opened issue #390 for intercity bus support in SeatMap: attributes: string[] for multiple simultaneous properties, a legend and tone hook per attribute, and a front or orientation slot. The grid abstraction created in #114 carried across three modes, while the attribute abstraction remained shaped by air. Different layers of one component generalized differently.
Two more row and bar gaps in the booking flow
- 🟡
RadioRoworSelectableListItem. The agent hand-rolled boarding and alighting stop pickers.RadioCardlooks like a priced product,ListCard.onSelectusesaria-pressedtoggle semantics rather than a single-choice radio, andRadioGroupis a bare input. A row with radio semantics, title, subtitle, and trailing metadata is useful across booking products. I recorded the row counterpart toRadioCardand the accessible single-choice counterpart toListCardon issue #391. - 🟡
BottomActionBar. The agent again hand-rolled a sticky footer with fare and action button. After disruption rebooking in #125, refunds in #129, and intercity bus here, this was the third case. I added the bottom counterpart toPageHeader, including safe-area insets, to issue #391.
Nothing new was added under src this round. The 4/5 confirmed five booking foundations across industries and recorded the attribute gap in SeatMap, RadioRow, and BottomActionBar.
What I took away: one component can generalize at one layer and not another
SeatMap, built in #114 and used in air, brought its grid abstraction into bus intact. Its semantic abstraction, the meaning and legend of seat attributes, remained shaped by air. That introduced a more precise way to measure cross-industry strength:
- Structural abstractions such as grids, rows against time, and distance against time can generalize from the start.
- Semantic abstractions such as seat attributes, state vocabulary, and legend labels tend to retain the first industry and reveal hand-rolling in the next mode.
Cross-industry testing is therefore not a yes-or-no question about whether a component works. It asks which abstraction layers carry. SeatMap scored well structurally and weakly semantically. A gap can be a missing component, or one layer of an existing component that has remained industry-specific.
📊 Component scoreboard (12 built)
AmountBreakdown / ActionQueue / ListCard / Gantt-segments / SeatMap / LoyaltySummaryCard / RadioCard / FilterChips / PageHeader / Itinerary / TicketStub / Stringline In progress: BottomActionBar approaching three of three, StatusLevel at two of three, ExpiryBadge at two of three, StatusBoard at two of three, intercity bus support in SeatMap, and RadioRow.
📋 Mode progress
- ✈️ Air: five staff + six passenger screens ✅ / 🚆 Rail: five staff + six passenger screens ✅
- 🚌 Bus: two staff + two passenger screens (arrivals and intercity booking) / remaining work or the next mode
Next up: build log #140
- The new mode, taxi, with staff-facing dispatch for real-time vehicle and order matching or passenger-facing ride hailing. A dispatch board could provide the third
StatusBoardcase.
The run
- gunjo.jp / SeatMap / RadioCard / Stepper / npm
@gunjo/ui/ GitHub / previous rounds #1 through #138 - GunjoUI by UIXHERO
The booking foundation crossed intact, while SeatMap showed a reusable grid with airline-specific meaning. Cross-industry strength depends on how far each abstraction layer carries.
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.