Planning a html/dom writer (website redesign part 3)

About
About 10 days ago now I did some planning for the wasm interface of my new website backend. I am going to start implementing it today. Thus I am going to mocking up the dom class so I can get basic html generation.
The root
I want to treat every page as its own "document", so I will need a root class for that. It will only be able to have one child element since you wrap stuff in the html tag in html. Its also problaby important to note this will be invisible from the web developer.

The public functions for this will problaby be somehting like this:
const Document = struct {
    allocator: std.mem.Allocator,
    root: Element,

    // NOTE: This adds a single html element
    pub fn init(allocator: *std.mem.Allocator) !Document;
    pub fn deinit(self: *const Document) void;

    // Basic zig format function, outputs in html.
    pub fn format(...) void;
};
Thats about it for the root. Next up is elements, I talked a bit about the structure of these in part 1, now its time to reify them a bit more. Im thinking something like this:
const Element = struct {
    const Data = union(Kind) {
        const Kind = enum { tag, attribute, text, generator };

        tag: struct {
            kind: []const u8,
            children: std.ArrayList(Element),
        },
        attribute: struct {
            key: []const u8,
            value: []const u8,
        },
        text: []const u8,
        generator: Generator,
    };

    allocator: std.mem.Allocator,
    data: Data,

    pub fn init(allocator: *std.mem.Allocator, new_data: Data) !Element;
    pub fn deinit(self: *const Element) void;

    pub fn addChild(self: *Element, child: Data) !void;
    pub fn addChildren(self: *Element, children: []const Data) !void;

    // Basic zig format function, outputs in html.
    pub fn format(...) void;
};
Note a few key differences, there is now a wrapper for it all, this is just so I can init data easier without allocation.

Finally I need a new Element Generator struct of some sort, really this is what part 2 was, but ill put it here with some cleanup:
const Generator = struct {
    tag: []const u8,

    pub fn init(base: anytype) Generator;

    // Basic zig format function, outputs in html.
    // This will not emit the wasm in generation builds
    // rather it will emit the call.
    pub fn format(...) void;
};
What this is all setting up for is a system where I can have everything in one place:
// index.zig
// generates index.html
const web = @import("web_library");
const main = web.mainFn(@This());

// This function runs with the generator, it emits the html
pub fn page(root: web.Element) !void {
    try root.addChildren(&.{
        .tag("h1", .{.text("This is a test page.")}),
        .gen("p", .runtime, struct {
            // This could also be an @import()

            pub fn page(root: web.Element) !void {
                const browser = web.getBrowser();
                try root.addChildren(&.{
                    .text("Hello from "),
                    .text(browser),
                    .text("!"),
                });

                // This is ran when a page is loaded, so be sure to keep it
                // breif. It is especially useful for registering callbacks
                // web.addCallback will have to emit javascript add event
                // on wrapper for a wasm call
                web.addCallback(.on_click, &onClick);
            }

            fn onClick() void {
                web.log("user clicked!", .{});
            }
        }),
        .gen("p", .generated, struct {
            // This could also be an @import()
            // might be useful for templating when its not runtime
            
            pub fn page(root: web.Element) !void {
                try root.addChildren(&.{
                    .text("How are you today"),
                });
            }
        }),
        .tag("p", .{.text("Ok tkx bye")}),
    });
}
Now that I have a full prototype for a first page im in a really good place to start implementing, and a metric for that progress. But thats all for now, so see yah next time where Ill update on how this implementation is going, and problaby link my github.

Links

Last Modified 2026 04/20