Creating Pages

The Quickstart guide shows how to make a simple page using Blu.

This section goes into greater depth on how to generate user interfaces using Blu.

HTML Elements

The Quickstart guide shows how to make a simple page with html, head, and body elements:

from blu.html import body, head, html


def __page__():
    return html[
        head,
        body['Hello World!'],
    ]

You can import any HTML tag from blu.html:

from blu.html import (
    html, head, body, div, span, select, canvas, mymadeuptagname
)

html[
    head,
    body[
        div,
        span,
        select,
        canvas,
        mymadeuptagname,
    ],
]
<html>
    <head></head>
    <body>
        <div></div>
        <span></span>
        <select></select>
        <canvas></canvas>
        <mymadeuptagname></mymadeuptagname>
    </body>
</html>

If the HTML tag name you want to import is a reserved word in Python, add a trailing underscore to the import name:

from blu.html import del_

del_['Hello, World!']
<del>Hello, World!</del>

All non-trailing underscores will be converted to dashes:

from blu.html import tag_name_with_dashes

tag_name_with_dashes['Hello!']
<tag-name-with-dashes>Hello!</tag-name-with-dashes>

In the rare case your desired tagname cannot be imported from the blu.html module, use blu.create_rare_html_element().

HTML Attributes

You can set the HTML attributes of an element by calling it as a function:

from blu.html import div

div(id='my-id')
<div id="my-id"></div>

Note

Calling an HTML element as a function does not mutate the original; instead, it returns a copy with the new attributes.

from blu.html import div

without_attributes = div
with_attributes = div(a='1', b='2')

without_attributes  # <div></div>
with_attributes  # <div a="1" b="2"></div>

Note

When you set HTML attributes on an element, you completely replace the existing attributes, rather than adding attributes on top:

from blu.html import div

original = div(a='1', b='2')

original(c='3', d='4')  # <div c="3" d="4"></div>

Note

Blu HTML elements take the same attributes as React HTML elements, not native HTML elements (see https://react.dev/reference/react-dom/components for more information):

# Wrong!
div(class='my-class')

# Right.
div(className='my-class')

Note

Event-handling attributes like “onClick” are only supported in client-side rendering (see Client-Side Rendering for more details).

from blu.html import div

def log_clicked(e):
    print('Clicked!')

# Only allowed in client-side rendering.
div(onClick=log_clicked)

To include an attribute name that is a reserved word in Python, add a trailing underscore:

from html import form, input, label

form[
    label(for_='email-address-input')['Name:'],
    input(id='email-address-input'),
]
<form>
    <label for="email-address-input">Value:</label>
    <input id="email-address-input"></input>
</form>

All non-trailing underscores will be converted to dashes:

from html import input

input(data_value=23)
<input data-value="23"></input>

You can also directly set an attribute name by passing a Mapping as the first positional argument:

from html import div

div({'attr_1_': 'value 1'}, attr_2_='value 2')
<div attr_1_="value 1" attr-2="value 2"></div>

Children

You can add children to an html element using square bracket notation:

from blu.html import div, p

div[
    p['Hi.'],
    p['Hello.']
]
<div>
    <p>Hi.</p>
    <p>Hello.</p>
</div>

Note

Using square bracket notation on an HTML element does not mutate the original; instead, it returns a copy with the new attributes.

from blu.html import div

without_children = div
with_children = div['Hello, World!']

without_children  # <div></div>
with_children  # <div>Hello, World!</div>

Note

When you set children on an HTML element, you completely replace any existing children, rather than appending to them:

from blu.html import div

original = div['A', 'B']

original['C', 'D']  # <div>CD</div>

A child of an HTML element can be any of the following types:

from blu.html import div, span, p

div[
    span,
    'Hello!',
    None,
    1,
    1.0,
    (
        p,
        'Hello again!',
        None,
        2,
        2.0,
    )
]
<div>
    <span></span>
    Hello!11.0
    <p></p>
    Hello again!22.0
</div>

Note

Items in Iterable children (other than strs and tuples) must be keyed:

from blu import Key
from blu.html import div

PEOPLE = [
    {'id': 0, 'name': 'Ana'},
    {'id': 1, 'name': 'Bill'},
    {'id': 2, 'name': 'Charlotte'},
]

# Wrong!
div[
    [
        div[f'Hello, {person["name"]}!']
        for person in PEOPLE
    ],
]

# Right.
div[
    [
        div(key=person['id'])[f'Hello, {person["name"]}!']
        for person in PEOPLE
    ],
]

# Wrong!
div[
    [f'Hello, {person["name"]}!' for person in PEOPLE],
]

# Right.
div[
    [
        Key(person["id"])[
            f'Hello, {person["name"]}',
        ]
        for person in PEOPLE
    ],
]

# Right.
div[
    (
        'Hello, Ana!',
        'Hello, Bill!',
        'Hello, Charlotte!',
    ),
]

# Right.
div['Hello, Ana! Hello, Bill! Hello, Charlotte!']

The rationale here is that, in client-side rendering, items in a sequence may be moved around in response to user interaction. Giving keys to these items allows React to maintain state and render efficiently even when an item’s position in a sequence changes.

You can tell Blu that items’ positions won’t change by putting them in a tuple. If you do this, you won’t have to key the items. For those familiar with React, this is how React fragments are defined in Blu.