Skip to content
ONIWC
iwc 0.1.0

Tutorial 2: Build your first UI page

Objective: add a new screen at /menu that lists dishes in a responsive grid with formatted prices, and cover it with a test.

Prerequisites: the hello project from Tutorial 1, with iwc dev running.

Step 1: Create the screen

Create ui/menu.ui.iwc. The file name is yours; the .ui.iwc extension makes it a screen, and route decides its URL. No registration is needed.

ui/menu.ui.iwc
{
  "name": "Menu",
  "route": "/menu",
  "layout": "main.layout.iwc",
  "title": "Our menu",
  "description": "Seasonal bowls and slow coffee, served all day.",
  "sections": [
    {
      "type": "section",
      "className": "menu",
      "layout": { "direction": "column", "gap": "24px" },
      "elements": [
        { "type": "text", "variant": "eyebrow", "content": "Served 8am – 10pm" },
        { "type": "text", "variant": "h1", "content": "Our menu" },
        {
          "type": "text",
          "className": "lead",
          "content": "Everything is made in our kitchen from seasonal produce. Ask us about allergens."
        },
        {
          "type": "grid",
          "columns": 3,
          "responsiveColumns": { "tablet": 2, "mobile": 1 },
          "gap": "16px",
          "data": {
            "source": "STATIC",
            "items": [
              { "name": "Green Goddess Bowl", "price": 349, "note": "Avocado, chickpeas, greens" },
              { "name": "Smoky Paneer Wrap", "price": 279, "note": "Charred paneer, mint chutney" },
              { "name": "Cold Brew", "price": 189, "note": "Steeped for 18 hours" }
            ]
          },
          "item": {
            "type": "column",
            "className": "dish",
            "layout": { "direction": "column", "gap": "6px" },
            "elements": [
              { "type": "text", "variant": "h3", "bind": "name" },
              { "type": "text", "variant": "caption", "bind": "note" },
              { "type": "text", "variant": "label", "bind": "price", "format": "currency", "currency": "INR" }
            ]
          }
        },
        { "type": "button", "label": "Back to home", "variant": "secondary", "action": "navigate", "target": "/" }
      ]
    }
  ]
}

Open http://localhost:3000/menu.

Step 2: Understand the screen

PropertyMeaning
nameA name for tooling; also the page title when title is missing
routeThe URL. /menu/:slug would capture a parameter
layoutWraps the screen in main.layout.iwc (the footer from Tutorial 1)
title, descriptionThe <title> (through head.title, so "Our menu · Hello") and the meta description
sectionsThe content, top to bottom

Inside the section:

  • Text variants choose the HTML tag: eyebrow is a small label above a heading, h1 the page heading, and paragraph (the default) a <p>. See the text element.
  • The grid has 3 columns, 2 below the tablet breakpoint (1024px) and 1 below mobile (640px).
  • data + item make the grid data-driven: STATIC reads the items written in the file, and item is repeated once per record. Later tutorials use DATABASE. and API. sources in exactly the same place.
  • bind puts a record's field into an element. format: "currency" with currency: "INR" turns 349 into ₹349.00.

Step 3: Style it

Append to styles/main.css:

styles/main.css
/* --------------------------------------------------------------- menu */
.menu { width: 100%; max-width: 960px; margin: 0 auto; padding: 48px 16px; }
.dish { padding: 20px; border: 1px solid var(--color-border); border-radius: var(--radius); background: var(--color-surface); }
.dish h3 { margin: 0; }

The page restyles in place as you save. var(--color-border) and friends come from the theme tokens in app.iwc, including their dark-mode values.

Step 4: Add a test

Create tests/menu.test.iwc:

tests/menu.test.iwc
{
  "name": "menu",
  "cases": [
    {
      "name": "the menu lists every dish with its price",
      "request": { "path": "/menu" },
      "expect": { "status": 200, "contains": ["Our menu", "Green Goddess Bowl", "Cold Brew", "₹349.00"] }
    }
  ]
}
Terminal
iwc test
Output
  ✓ menu › the menu lists every dish with its price
  ✓ smoke › home renders the welcome page
  …
7 passed, 0 failed

To link the page from the home screen, add a button to the actions row in ui/home.ui.iwc:

element (in a .iwc file)
{ "type": "button", "label": "See the menu", "variant": "secondary", "action": "navigate", "target": "/menu" }

IWC checks links: a target route that no screen has is reported as a warning by iwc check.

Troubleshooting

MessageFix
a list data source needs an "item" element to repeatA container with data needs item.
text elements need "content" or "bind"Each text element shows literal content or a bound field.
Prices show as plain numbersAdd "format": "currency" and a currency code.

Next: Create a reusable button.

Source: apps/marketing/content/docs/tutorials/first-page.md