Custom components
Say your docs need a callout box on 40 pages. Pasting its HTML and CSS 40 times works until the day you change it. In chimera you write the callout once as a class that gets the same methods, type checking and theming as the built-in components.
A component
Implementing Component lets your class go anywhere content goes, and Container lets it hold children. tag names the HTML tag it renders as:
class Callout implements Component, Container
function tag(): string
return "aside"
end
function __init(self, title: string?, content: Content?)
self:add(Strong(title), content)
end
function default_styles(self): { Rule }
const callout = self:style_selector()
return {
Rule(callout, {
display = "block",
padding = "0.75rem 1rem",
border_left = `4px solid {token("primary")}`,
background = token("surface"),
border_radius = token("radius"),
}),
Rule(`{callout} > p`, { margin = "0.25rem 0 0" }),
}
end
end
const warning = Callout("Heads up", Paragraph("Saves from version 1 don't load in version 2."))
default_styles returns the CSS the component needs, and every page that uses the component includes those rules once. style_selector() gives the selector for the class's elements. Rules built on it only ever match your component.
Parsing it back
Implementing Component records the class under its name as soon as the class is defined. Its elements render with data-element="Callout", and the parser uses that attribute to rebuild them as Callouts (see Parsing HTML). A class that should own a plain tag, so every <meter> parses as it, implements HtmlElement as well.
Arranging children
Implement Layout instead of Container and your component also gets row(), grid(), gap() and the rest of the methods Layout describes.
Scripts and stylesheets
When a component needs a script or a stylesheet in the page's <head>, return it from head_elements. A page gets each one once, however many of the components it holds:
function head_elements(self): { Metadata }?
return { Script(nil, "https://cdn.example.com/charts.js"):defer() }
end
For a small handler, on("click", script) is enough. It writes the code into the element's onclick attribute.
Rendering your own HTML
By default a component renders its tag, its attributes and its children. For anything else, override render and use render_open to write the opening tag:
function render(self, out: Writer)
self:render_open(out)
out:write(`<span class="count">{#self.children}</span>`)
for _, child in self.children do
child:render(out)
end
out:write("</aside>")
end
The Writer knows about the page being rendered, including its path, the site's pages and the themes on offer. out:href_to(path) turns a path from the site's root into a link that works from the current page.
Printing in the terminal
print(warning) shows the element in the terminal as a tree rather than as HTML. Override outline to change the one-line summary each node gets.