Victor PereiraTechnical WritingsTutorial · Part 03

Kiro IDE: Creating a First Project — Part 3

From reviewed specifications to an implemented manufacturers browser: inspecting generated files, completing missing behavior, and testing the result.

Kiro Part 3: HTML, CSS, and JavaScript connected to a manufacturers table.
From reviewed specifications to implementation. Conceptual cover illustration, not an application screenshot.

When the plan meets the generated files

Part 1 established steering context. Part 2 developed and reviewed the feature requirements, technical design, and task plan. This stage examines what happened when implementation tasks were run.

The objective remains a single screen for browsing vehicle manufacturers from the NHTSA vPIC API. Users move between pages using Previous and Next controls, with readable loading messages and a way to retry failed requests.

An execution limit changed the workflow: the available free Kiro credits were exhausted before the application was complete. The next step was to inspect the actual artifacts, identify the missing work, and continue from the specification.

This article distinguishes the Kiro-generated starting point from the subsequent completion work. It does not present the final application as an uninterrupted Kiro implementation.

1. Inspect the artifacts before deployment

The supplied files contained a semantic HTML skeleton, responsive CSS, and an empty JavaScript file.

Supplied fileObserved stateConsequence
index.htmlPage title, pagination controls, status region, and tableThe interface structure exists
css/style.cssMobile-first styling and a 600px breakpointThe basic presentation exists
js/app.js0 bytesNo requests, rendering, navigation, or retry behavior

Uploading this starting point would display an empty table and a label reading “Loading…”, with both buttons disabled. A script tag pointing to a file is not evidence that application behavior has been implemented.

The starting files are still useful evidence for a technical tutorial. They show a partially executed plan and make the remaining work concrete. They are not sufficient for deploying a functioning manufacturers browser.

2. Continue from the specification with an explicit handoff

Codex completed a separate deployment copy after the credit limit was reached. The original repository files were preserved. This provides a clear boundary between generated work, reviewed work, and later changes.

ContributorWork represented in this handoff
KiroSteering and specification workflow; supplied HTML and CSS starting point
CodexArtifact inspection, JavaScript implementation, focused HTML/CSS adjustments, local browser verification, and this tutorial
Deployment operatorUpload, production-origin integration checks, and release decision

The reviewed specification remained the baseline. Completing the application did not require introducing a framework, a database, or a build system.

manufacturers-browser/
├── index.html
├── css/
│   └── style.css
├── js/
│   └── app.js
└── DEPLOYMENT.md

The deployment notes describe provenance, verification evidence, remaining integration checks, and known limitations. They are handoff documentation rather than a runtime dependency.

3. Implement the request and display loop

The completed script automatically requests page 1. A successful response is validated before it replaces the table, and each navigation request retrieves one API page.

The official Get All Manufacturers reference documents 100-item pages and the page parameter. The application does not rely on 232 as a fixed final page and does not display an unverified total. NHTSA vPIC API reference

Open page → request page 1 → validate response → render table
Next      → request current page + 1 → validate → replace results
Previous  → request current page - 1 → validate → replace results
Failure   → preserve displayed results → offer Retry

Separate state values represent the displayed page, requested page, and failed target. The distinction becomes important when page 2 is visible but the request for page 3 fails: the table and label must continue to describe page 2, while Retry must request page 3.

The code is contained in an IIFE. It uses native browser features and adds no third-party runtime dependency.

4. Preserve state when requests fail

The request handler uses a single try/catch/finally structure. A timer remains active while the response body is read, and cleanup runs after success or failure.

try {
  const response = await fetch(url, { signal: controller.signal });
  if (!response.ok) throw new Error('HTTP error: ' + response.status);
  const data = await response.json();
  validate(data);
  // Apply the validated response to the interface.
} catch (error) {
  // Preserve results and record the failed target for Retry.
} finally {
  clearTimeout(timeout);
  state.loading = false;
  updateButtons();
}

This excerpt illustrates the control flow; the delivered script contains the full handlers and user messages. AbortController can cancel both the fetch and consumption of the response body, which matters when headers arrive but the body stalls. AbortController documentation

The delivered timeout is 10 seconds. A failed initial load leaves both navigation buttons disabled and offers Retry. A later failure preserves the previously displayed table. Repeated retries retain the same failed target until a successful response is received.

Next and Previous are evaluated independently. Both are disabled during an active request, preventing overlapping navigation from replacing results out of sequence.

5. Validate data before replacing results

A successful HTTP status and valid JSON do not establish that a response is usable. The implementation checks that Results is an array, Count is an integer between 0 and 100, and the count matches the array length.

Record validation checks the fields needed for display, including a non-empty full manufacturer name and an array of vehicle types. Malformed records produce a recoverable response error rather than being silently discarded. This resolves the validation policy identified at the end of Part 2.

Each displayed cell is created as a DOM element and assigned text:

const cell = document.createElement('td');
cell.textContent = value;

Manufacturer names and other API fields are therefore displayed as text rather than interpreted as HTML. The automated checks include a name containing an HTML-like payload and verify that no image element is created.

The name column uses the common name when non-empty and falls back to the full name. Null countries and empty vehicle-type arrays display as empty cells.

6. Apply pagination boundaries without inventing a total

Boundary handling follows the reviewed application assumption that API pages form a contiguous sequence:

ResponseBehavior
100 recordsDisplay results and permit probing the next page
1–99 recordsDisplay results and record the boundary
Empty page after a full pagePreserve displayed results and disable Next at that boundary
Failed or invalid responsePreserve results and provide Retry

A full page does not prove another populated page exists. An empty probe is the expected discovery case when the final populated page contains exactly 100 records.

Navigating backward to an earlier full page preserves the later boundary. A full response at or beyond a previously recorded boundary can invalidate it. These rules are a practical browsing strategy, not a guarantee of snapshot consistency while the upstream registry changes.

The label remains “Page N”. The empty initial dataset has a separate “No data” state.

7. Complete presentation and accessibility behavior

The Kiro-generated markup already included native buttons, column-header scope attributes, and a status live region. The completion work retained that structure and added request-state signaling through aria-busy.

Focused CSS adjustments allow column headings to wrap, strengthen keyboard focus outlines, and keep navigation and retry targets at least 44 pixels high. The status region remains present, and successful loads provide an explicit message instead of relying on silence to signal completion.

After user-triggered navigation renders new records, focus moves to the first data cell. Initial automatic loading does not move focus into the table.

These are implemented behaviors, not a claim of comprehensive accessibility certification. Screen-reader announcements and navigation should still be checked manually in the deployed application.

Completed manufacturers screen displaying seven controlled test records, including Example Motors.
Actual browser capture using seven fixture records. This is test data, not live NHTSA data. Next is disabled because this fixture is a partial page.

8. Verify the behavior with controlled responses

Local checks ran in headless Chrome against a controlled HTTP endpoint. The test server supplied representative success and error responses; it did not query the live NHTSA registry.

Ten behavioral and layout checks passed, and an additional check captured the application screenshot used in this article.

Verification scenarioResult
Full initial page, fallback names, and safe text renderingPassed
Forward/back navigation, partial boundary, and focusPassed
Empty probe preserves results and boundary through backward navigationPassed
Initial failure disables navigation; Retry requests page 1Passed
Repeated retries request the failed pagePassed
Malformed JSON produces a recoverable errorPassed
Invalid count or malformed record preserves the displayed resultsPassed
Stalled response body times out and releases loading statePassed
Empty initial dataset disables navigationPassed
320px layout and 44px navigation targetsPassed

The timeout check used a test-only 350ms limit to exercise cancellation quickly. The delivered application retains the 10-second setting. The screenshot shows seven fixture records, including “Example Motors”; it is not a capture of live registry data.

Browser checks establish evidence for these scenarios. They do not establish production CORS access, cross-browser coverage, or screen-reader behavior.

9. Upload to staging and verify the real integration

The completed copy is ready for a staging upload. Preserve the relative paths when uploading index.html, css/style.css, and js/app.js into a dedicated public subdirectory.

Use a staging directory first so the existing traffic endpoint is not replaced accidentally. Open the uploaded page over HTTPS and check the first load, Next, Previous, and browser console.

The JavaScript defaults to direct NHTSA access with USE_PROXY = false. Production-origin CORS has not been verified in this handoff. If browser access is blocked, deployment is incomplete until a same-origin PHP proxy that accepts and forwards the page parameter is implemented and verified. The package does not include that conditional proxy; do not enable the switch before one exists.

No build step or Node server is needed for the deployed application. The local browser-test tooling is separate from its runtime files.

Where Part 3 ends

The initial Kiro output was a useful interface skeleton, but an empty JavaScript file made it unsuitable as a functioning deployment. A documented handoff completed the missing behavior, retained the minimal stack, and produced passing local checks.

The result is a staging candidate with explicit evidence and limits. Production integration remains a separate checkpoint, and this article does not claim that a live deployment has been completed.

The engineering lesson is practical: inspect the generated artifacts, trace the remaining work back to the specification, verify concrete behavior, and keep authorship and deployment status clear when execution continues across tools.

Project repository

All project files—including the steering documents, feature specifications, and deployment files—can be found in the kiro_traffic repository on GitHub.