Content.js
The complete source of src/Content.js, exactly as it ships, in reading order. Every line is present; the file is split at its own section banners.
Content.js - the content primitives (text, headings, panels, code)
// ============================================================================
// Content.js - the content primitives (text, headings, panels, code)
// ============================================================================
//
// The semantic content tier: text, titles, labels, code, cards and sections. Built
// entirely from the Dom.Tag DSL - each function is just "which element, which class".
// No layout decisions live here; how these sit is Layout.js and the stylesheet.
//
// Every function declares its inputs with Reactive.Argument. A `text` may be a plain
// string or a reactive Text node; a `content` is a builder callback run inside the tag.
//
// Content.Text(text) a run of text Content.Panel(content) a card
// Content.Title(text) a heading Content.Section(content) a section
// Content.Label(text) a form-field label Content.Tabset(selected, body) a tab switch
// Content.Code(text) a read-only monospace block (preserves newlines)
// Content.Tabset.Tab(id, label, panel) one entry of a Tabset (inner to Tabset)
// ----------------------------------------------------------------------------
Reactive.Module("Content", () => {
// Text - a run of text. Renders: <span>text</span>.
Reactive.Function("Text", () => {
const text = Reactive.Argument(Text);
Dom.Tag("span", () => {
Dom.Text(text);
});
});
// Title - a heading over a section or panel. Renders: <h3>text</h3>.
Reactive.Function("Title", () => {
const text = Reactive.Argument(Text);
Dom.Tag("h3", () => {
Dom.Text(text);
});
});
// Label - a form-field label beside a control. Renders: <label>text</label>.
Reactive.Function("Label", () => {
const text = Reactive.Argument(Text);
Dom.Tag("label", () => {
Dom.Text(text);
});
});
// Code - a read-only monospace block; <pre> preserves newlines (a GLSL signature
// header). Distinct from the EDITABLE leaf Input.Code.Glsl. Renders: <pre class="code">.
Reactive.Function("Code", () => {
const text = Reactive.Argument(Text);
Dom.Tag("pre", () => {
Dom.Attribute("class", "code");
Dom.Text(text);
});
});
// Panel - a card that visually groups its content. Renders: <div class="panel">.
Reactive.Function("Panel", () => {
const content = Reactive.Argument(Reactive.Callback());
Dom.Tag("div", () => {
Dom.Attribute("class", "panel");
content();
});
});
// Section - groups related content (usually under a Content.Title). Renders: <section>.
Reactive.Function("Section", () => {
const content = Reactive.Argument(Reactive.Callback());
Dom.Tag("section", () => {
content();
});
});
// Tabset - a tab strip over a switched panel, bound to `selected`. `body` declares the
// entries with Content.Tabset.Tab (and may group them with Content.Section). The button
// strip AND the panel switch are ONE declaration: each Tab emits its own control and,
// only while it is the selected tab, its panel - so exactly one panel is ever in the tree
// and the strip/switch can never drift (the two-parallel-lists problem is gone). How the
// strip and the panels are arranged (strip on top or on the left, panel in the content
// area) is the stylesheet's job, targeting .tabset / .tab / .tabpanel. Renders:
// <div class="tabset"> button.tab* + div.tabpanel(active) </div>.
Reactive.Function("Tabset", () => {
const selected = Reactive.Argument(Reactive.Any);
const body = Reactive.Argument(Reactive.Callback());
// Tab(id, label, panel) - one entry. Defined inside Tabset so it captures `selected`
// by closure (the settled no-ambient-context pattern, as Button.Select.Option does).
// The control selects this tab on click and marks itself active while selected == id;
// the panel is rendered only while active, so it is the one-of switch.
Reactive.Function("Tab", () => {
const id = Reactive.Argument(Reactive.Any);
const label = Reactive.Argument(Text);
const panel = Reactive.Argument(Reactive.Callback());
const active = Text.Equal(selected, id);
Button.Set(selected, id, () => {
Dom.Attribute("class", "tab");
Dom.Attribute("aria-selected", active);
Content.Text(label);
});
Reactive.If(active, () => {
Dom.Tag("div", () => {
Dom.Attribute("class", "tabpanel");
panel();
});
});
});
Dom.Tag("div", () => {
Dom.Attribute("class", "tabset");
body();
});
});
});