Round #20Score 4.5/5Foundation / generic UI

An empty dashboard ended the clean streak with a missing heading

Route: /dashboard-empty
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

An empty dashboard ended the clean streak with a missing heading

⁠Build log series⁠: I hand a real screen to an AI that has never seen Gunjo (@gunjo/ui) and let it build. This round covered an ⁠empty dashboard for a first-time user⁠.

The agent saw the same limited inputs: the shipped package and docs, not the source. The /dashboard-empty assignment called for a welcome banner, Statistic components with zero values, charts with no data, an onboarding checklist with Progress, an empty activity area with EmptyState, and a full-page StatusScreen.

Rounds and had produced ⁠no new class of problem twice in a row.⁠ This was the third attempt, meant to determine ⁠whether the library had really stopped turning up anything new.⁠

Result: 4.5/5, but the clean streak ended

tsc and build passed, the console stayed clear, the server and browser output matched with no hydration errors, and nothing overflowed at 375px. The functionality was solid. ⁠But the screen exposed one new class of accessibility failure.⁠

The best result: charts handled empty data safely

The central question for a first-run dashboard was whether the charts would break with no data. The real-browser results were strong:

  • ⁠There was no NaN anywhere in the DOM.⁠ When every value was zero, BarChart gave each bar height:0% and kept the axes as the intended empty display.
  • ⁠With every value at zero, DonutChart reached an explicit if(every value<=0) return fallback guard in circular-chart-utils.ts.⁠ ⁠The component deliberately prevents a divide-by-zero crash.⁠
  • With data={[]}, SparklineChart rendered an empty line and no NaN. It uses span = max-min || 1.

Charts often fail on empty data. Gunjo ⁠guards this case explicitly⁠, which earned substantial credit.

Why the streak ended: a new accessibility failure

⁠The titles in EmptyState and StatusScreen were not headings.⁠ The source showed:

  • EmptyState.tsx:32: <p className="text-sm font-semibold">{title}</p>
  • StatusScreen.tsx:129: <p className="text-2xl font-semibold">{resolvedTitle}</p>

A full-page StatusScreen such as "No projects yet" therefore had ⁠no <h1> at all.⁠ A screen-reader user navigating by heading would find no structure on a screen with meaningful content, violating WCAG 2.4.6 and 1.3.1. The API offered no escape hatch such as headingLevel or as. I logged issue #104.

⁠This was the sixth distinct accessibility failure, following issues #57, #71, #83, #87, and #93.⁠ Empty-state components are the focus of a first-run screen, but they were secondary on the profile and settings screens in and . ⁠This assignment was the first to expose the missing structure.⁠

Other minor findings: Progress had no divide-by-zero guard when max=0 (issue #105); Badge had no success tone even though Statistic did (issue #106); and several docs pages had the wrong import path (issue #50).

What I took away: do not declare the work finished too early

I nearly treated the two clean rounds, and , as proof that nothing new remained. ⁠Round #20 disproved that conclusion.⁠ The same thing had happened when looked like the home stretch and found another failure.

Functionality: no new failures, with no crashes, NaN, or type mismatches and robust empty chart data
Accessibility: still producing a new case when the screen type changes, now for the sixth time
Docs: still behind, with incorrect imports and no empty-data examples

⁠Functionally, the components had stopped producing new problems. Accessibility coverage had not.⁠ Each new kind of screen brought a different component to the foreground and exposed another missing accessibility behavior. That meant ⁠the series needed to continue.⁠ I recorded the sixth failure plainly and fixed it.

Every defect I fixed stayed fixed in the next round, but each new kind of screen still revealed a different gap. That was proof that the work was not finished.

⁠I added headings in issue #104 and the Progress guard in issue #105, shipping both in PR #108.⁠ A real-browser check confirmed <h2> in every EmptyState demo and <h1> in all six StatusScreen variants. In parallel, the Slider fix from issue #100 shipped in PR #107.

Next up: build log

  • ⁠A blog post, docs article, or landing page⁠, continuing the accessibility coverage with another kind of screen.

The run

  • gunjo.jp / npm @gunjo/ui / GitHub
  • Earlier rounds: through
  • Overview: Gunjo (@gunjo/ui) / Why it exists: Design systems in the age of AI
  • ⁠GunjoUI by UIXHERO

Gunjo was still in early alpha at the time; issues are welcome.

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指示書パック」、先行登録を受付中です。