;; ---------------------------------------------------------------------------
;; Spreads — child-to-parent communication across render boundaries
;; ---------------------------------------------------------------------------
(defcomp ~geography/spreads-content ()
(~docs/page :title "Spreads"
(p :class "text-stone-500 text-sm italic mb-8"
"A spread is a value that, when returned as a child of an element, "
"injects attributes onto its parent instead of rendering as content. "
"This inverts the normal direction of data flow: children tell parents how to look.")
;; =====================================================================
;; I. The primitives
;; =====================================================================
(~docs/section :title "Three primitives" :id "primitives"
(p "The spread system has three orthogonal primitives. Each operates at a "
"different level of the render pipeline.")
(~docs/subsection :title "1. make-spread / spread? / spread-attrs"
(p "A spread is a value type. " (code "make-spread") " creates one from a dict of "
"attributes. When the renderer encounters a spread as a child of an element, "
"it merges the attrs onto the parent element instead of appending a DOM node.")
(~docs/code :code (highlight "(defcomp ~highlight (&key colour)\n (make-spread {\"class\" (str \"highlight-\" colour)\n \"data-highlight\" colour}))" "lisp"))
(p "Use it as a child of any element:")
(~docs/code :code (highlight "(div (~highlight :colour \"yellow\")\n \"This div gets class=highlight-yellow\")" "lisp"))
(p (code "class") " values are appended (space-joined). "
(code "style") " values are appended (semicolon-joined). "
"All other attributes overwrite."))
(~docs/subsection :title "2. collect! / collected / clear-collected!"
(p "Render-time accumulators. Values are collected into named buckets "
"during rendering and retrieved at flush points. Deduplication is automatic.")
(~docs/code :code (highlight ";; Deep inside a component tree:\n(collect! \"cssx\" \".sx-bg-red-500{background-color:hsl(0,72%,53%)}\")\n\n;; At the flush point (once, in the layout):\n(let ((rules (collected \"cssx\")))\n (clear-collected! \"cssx\")\n (raw! (str \"\")))" "lisp"))
(p "This is upward communication through the render tree: "
"a deeply nested component contributes a CSS rule, and the layout "
"emits all accumulated rules as a single " (code "