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:
Another
blu.HTMLElement.A
str. In this case, it will be rendered as an HTML text node.None. Nothing is rendered in the place where aNonevalue is found.A
int. This renders as a text node whose text is thestrrepresentation of the integer.A
float. This renders as a text node whose text is thestrrepresentation of the float.An
Iterableof valid children. This renders as all the child nodes contained in theIterable.A
blu.ClientElement(see Client-Side Rendering).A
blu.Key.
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.