The HTML Canvas Interface is WildCamping's fluent, object-oriented API for generating HTML elements. It provides a clean, chainable syntax for building dynamic and static HTML using Pharo methods, making UI construction intuitive and maintainable.
The WCHtmlCanvas class is the entry point for all HTML generation. It provides a fluent API where each method returns a brush object that can be chained to build complex HTML structures.
html div
id: 'myDiv';
class: 'container';
with: 'Hello World'Brushes are specialized objects that represent HTML elements. When you call a method on the canvas (like div, span, button), you get back a brush object that understands how to configure that element.
Common brush methods:
id:- Sets the element's ID attributeclass:- Adds CSS class(es)with:- Adds content (text or nested elements)style:- Inline CSS styles
html div
with: 'Simple text content'html button
id: 'submitBtn';
class: 'btn btn-primary';
with: 'Submit'Use blocks to nest elements:
html div
id: 'container';
with: [
html heading with: 'Title'.
html paragraph with: 'Some content'.
html button with: 'Click me'
]div- Generic containersection- Thematic grouping of contentspan- Inline container
heading- Header (h1-h6)paragraph- Paragraphbreak- Line breakhorizontalRule- Horizontal rulepreformatted- Preformatted textidiomatic- Idiomatic text
unorderedList- Unordered listlistItem- List item
table- Table elementtableHead- Table header sectiontableBody- Table body sectiontableRow- Table rowtableHeading- Table header celltableColumn- Table data cell
form- Form containertextInput- Text input fieldnumberInput- Number inputcheckbox- Checkbox inputfileInput- File upload inputinputRange- Range slidertextArea- Multi-line text areaselect- Dropdown listoption- Option in selectlabel- Form labelbutton- Button elementsubmitButton- Submit button
image- Image elementcanvas- HTML5 canvassvg- SVG containerpath- SVG path element
anchor- Hyperlinklink- External resource linkscript- Script element
html div
id: 'myId';
class: 'myClass otherClass';
style: 'color: red; font-size: 14px;';
with: 'Content'html div
attributeAt: 'data-value' put: 42;
with: 'Element with data attribute'html button
onclick: 'alert("Clicked!")';
with: 'Click me'html paragraph
with: 'Plain text content'html div
with: [
html paragraph with: 'First paragraph'.
html paragraph with: 'Second paragraph'
]html div
with: [
html text: 'Text before'.
html paragraph with: 'Paragraph'.
html text: 'Text after'
]html div
with: [
condition
ifTrue: [ html paragraph with: 'True branch' ]
ifFalse: [ html paragraph with: 'False branch' ]
]Any object that implements renderOn: can be rendered through the canvas:
html div
with: [ AComponentClass for: self on: html named: #myComponent ]Render collections of components:
html unorderedList
with: [
items do: [ :item |
html listItem with: (ItemComponent new item: item)
]
]html unorderedList
id: 'items';
with: [
#('Item 1' 'Item 2' 'Item 3') do: [ :item |
html listItem with: item
]
]html table
id: 'dataTable';
with: [
html tableHead
with: [
html tableRow
with: [
html tableHeading with: 'Name'.
html tableHeading with: 'Age'
]
].
html tableBody
with: [
people do: [ :person |
html tableRow
with: [
html tableColumn with: person name.
html tableColumn with: person age asString
]
]
]
]html form
with: [
html label
for: 'username';
with: 'Username'.
html textInput
id: 'username';
name: 'username'.
html label
for: 'password';
with: 'Password'.
html textInput
id: 'password';
name: 'password';
type: 'password'.
html submitButton with: 'Login'
]html svg
width: '100';
height: '100';
with: [
html path
d: 'M 10 10 H 90 V 90 H 10 Z';
stroke: 'black';
fill: 'none'
]"Good: Semantic structure"
html section
with: [
html heading
level: 1;
with: 'Article Title'.
html paragraph with: article text
]"Good: Clear nesting with blocks"
html div
with: [
html heading with: 'Title'.
html paragraph with: 'Content'
]
"Avoid: Excessive method chaining"
html div with: html heading with: 'Title'"Elements you plan to access from JavaScript should have IDs"
html button
id: 'myButton';
with: 'Click me'
"Later in start:"
(document getElementById: 'myButton')
addEventListener: 'click'
do: [ self handleClick ]In WildCamping components, use the passed canvas in renderHtmlOn::
MyComponent >> renderHtmlOn: html
"html is a WCHtmlCanvas instance"
html div
id: self elementId;
class: self cssClass;
with: self contentElements generated through the canvas are automatically tracked by the component's scoping mechanism, making them accessible via getElementById: in the start method.