Rendering pages

A page is built with the Ui library, not written as HTML. You compose static methods, each taking a closure for its content, and the library emits the markup, escapes your text, and gives every module the same look without a line of per-module CSS.

A page is a tree of calls

You describe a page as nested method calls rather than opening and closing tags by hand. UiContent::Area opens a region; UiContent::Title and UiContent::Text place a heading and copy; UiContent::Panel groups related content; UiContent::Link and UiContent::Button add navigation. Each call that has content takes a closure, and the calls inside that closure become the content nested within it, so the shape of the code matches the shape of the page:

UiContent::Area(function() {
    UiContent::Title('Products');
    UiContent::Text('Everything currently for sale.');
    UiContent::Link('Add a product', '/shop/add');
});

There are no tags to close and no class names to remember. Because the text you pass is escaped for you, you never hand-escape a value or risk forgetting to.

Why declare instead of write markup

Building a page through the library rather than echoing HTML is one instance of the framework's wider declarative style: you state what the page should contain and let the library work out how to render it. That is the same principle behind routing and the other interfaces, and it is what lets the framework treat a page as data it can gather and, in future, emit for the front end from the same PHP. The full reasoning lives on its own page; see Declarative code.

The library groups, by what they build

The Ui library is organised into classes of static methods, each named for the kind of thing it produces:

  • UiContent: areas, panels, sections, titles, text, links, buttons, menus and images, the everyday page body.
  • UiForm: text inputs, text areas, dropdowns, checkboxes, hidden and file inputs, and submit buttons.
  • UiTable: headers, rows and typed cells for text, numbers, booleans, code and buttons.
  • UiLayout: rows, columns, grids, stacks and sizing wrappers.
  • UiCard, UiNavigation and UiTabs: cards and card lists, menus with navigation items, and horizontal or vertical tabs.
  • UiHtml and UiPage: the outer page, its title, description, styles and scripts.

The ready-made building blocks are covered next, in Components and widgets.

Styling comes from the library

UiThemeService generates the presentation from the library's own semantic tags. You write the structure and the theme decides how it looks, so every module renders with the same style and the pages of a whole site stay consistent. This is why hand-written markup and invented class names do nothing here: the theme only knows the library's tags, so any HTML you write by hand is unstyled and unmaintainable. When the library cannot express something, the answer is to add a method to the Ui module, never to drop down to raw HTML.

A page area with a form

A typical page section opens an area, adds a title and some text, then renders a form with a few fields and a submit button. This mirrors the admin pages that ship with the framework, where UiForm::ActionForm takes one closure for the fields and a second closure that runs when the form is submitted:

UiContent::Area(function() {
    UiContent::Title('Add product');
    UiContent::Text('Enter a name and price to add a product.');

    UiForm::ActionForm('shop_product_add', function() {
        UiForm::Text('name', 'Name');
        UiForm::Text('price', 'Price (pence)');
        UiForm::Submit('Add');
    }, function() {
        Shop::AddProduct();
    });
});

Handling that submission, validating the fields, and redirecting is the subject of Forms and input.

Next: Components and widgets.