Components
Afia websites are made up of components.
For example, a simple web page might contain a nav bar component and a hero component, whereas a more complex website might contain dozens or hundreds of components.
Components can be connected to each other, forming a graph.
Imagine an Add 2 component that adds two to a number, a Multiply 5 component that multiples a number by 5. These components could be chained by connecting the Add 2's output to the Multiply 5's input.
(Input Number) -> Add 2 -> Multiply 5 -> (Output Number).
If the number 4 was passed into the Add 2 component's input, 30 would flow out of the Multiply 5 component's output.
Component ABI
Afia components are WebAssembly modules that conform to the Afia Component ABI.
Here is a simple component written in the WebAssembly text format:
(module
(@custom "AFIA_SCHEMA_VERSION" "\00")
(@custom "AFIA_INPUT_TYPE_500" "int64{min:0,max:1000}")
(@custom "AFIA_INPUT_SLUG_500" "addend")
(@custom "AFIA_OUTPUT_TYPE_1" "int64{min:2,max:1002}")
(@custom "AFIA_OUTPUT_SLUG_1" "addend-plus-two")
(import "afia_env" "Afia_Input_getI64") (func $get_i64_input (param i32) (result i64))
(func (export "Afia_initialize") (result i32) i32.const 12345)
(func (export "Afia_Output_1") (param $context i32) (result i64)
(i64.add
(call $get_i64_input (i32.const 500))
(i64.const 2)
)
)
)
The above component defines a single input with numeric ID 500. It also has a human-readable string ID "addend". Afia's tools use this slug when displaying the input.
The "addend" input accepts a 64-bit integer between zero and one-thousand, inclusive.
The "added-plus-two" output was given ID 1. It reads the input, adds two, then returns the summed integer. The output's type indicates that it can be any integer between [2, 1002], inclusive.
Writing Components
Most components are too complex to be reasonably maintained in the WebAssembly text format.
Instead, developers can use higher-level languages and tools to author their component, after which they can compile their component into a WebAssembly binary for Afia to consume.
Developer can use a programming language that compiles to WebAssembly, such as Rust or Zig. They can also use templating language, such as HTML or XML, and then compile those templates into WebAssembly.
Should they like, a developer could write a tool that downloads a file from their favorite design tool, parses it, and then generates the WebAssembly that Afia expects.
Afia is only concerned with WebAssembly. Developers are free to produce that WebAssembly however they please.
Packaging Components
A component package file contains a component's WebAssembly binary, CSS and metadata.
To create a component package via using the Afia CLI. For example:
afia component package --wasm=/path/to/file.wasm > my-component.comp
Component package files can be shared. By sharing components, users can leverage each other's work instead of duplicating their efforts.
DOM Access
Afia provides functions that components can use to access the DOM.
For example, components can import and call Afia_Dom_createElement to create a DOM element, and they can use Afia_Dom_setAttribute to set an attribute on the DOM element.
Each component has its own isolated view of the DOM. Imagine the following page that contains two components:
<body>
/* Component 1 */
<div id="component-1">
<span>This is Component One</span>
</div>
/* Component 2 */
<footer class="component-2">
<h3>This is Component Two</h3>
</footer>
</body>
The WebAssembly code for the first component can only see the <div> and the <span> that it created. It has no way of accessing the <footer> or <h3>.
The second component's code can see the <footer> and <h3>, but has no way to access the <div> or <span>.
This isolation prevents components from breaking each other, allowing users to use components written by different people without worrying about them conflicting in unexpected ways.
Here is a WebAssembly Text snippet from a component that outputs a DOM element that renders "hello world".
(import "afia_env" "Afia_Dom_createElement") (func $create_element (param i32 i32) (result i64))
(import "afia_env" "Afia_Dom_setInnerText") (func $set_text_content (param i64 i32 i32))
(data (i32.const 0) ("div"))
(data (i32.const 3) ("hello world"))
(func (export "Afia_Output_123") (param $context i32) (param $maybe_existing_dom_elem i64) (result i64)
local $new_dom_elem i64
(i64.eqz (local.get $maybe_existing_dom_elem))
(if (result i64)
(then
(call $create_element (i32.const 0) (i32.const 3))
(local.set $new_dom_elem)
(call $set_inner_text (local.get $new_dom_elem) (i32.const 3) (i32.const 11))
local.get $new_dom_elem
)
(else (local.get $maybe_existing_dom_elem))
)
)
When first called, the above component will create a new <div> element and set its inner text to "hello world".
On subsequent calls, the component will do nothing. A more component might rely on inputs and rerender itself if those inputs are changed.
CSS
Components can optionally include CSS.
One way to include CSS is to define a WebAssembly custom section:
(@custom "AFIA_CSS" ".my-class { color: red; } #my-id { font-size: 30px; }"
Another way to include a CSS file is write it to the AFIA_CSS_OUT_FILE environment variable when using the Afia CLI's afia component build command to interpret a Component.toml and produce a component package file.
Users can also include CSS when using the afia component package command:
afia component package --wasm=/path/to/file.wasm --css=/path/to/file.css
CSS Collision Resolution
Imagine the following page with two components:
<body> /* Component 1 */ <nav class="background">My Navigation</nav> /* Component 2 */ <footer class="background"></footer> </body>
Say that the Nav Bar component's CSS is .background { color: red }, and that the Footer's CSS contains .background { color: blue }.
Despite using the same "background" class name, the Nav Bar will be red, and the Footer will be blue.
Afia's CSS collision resolution automatically resolves conflicts between different components' CSS selectors.
In this way, components can use whichever selector names they please, with no fear of their styles colliding with other components.