From 41c3b9f3b83b901c6f2a6576ae98457abff40968 Mon Sep 17 00:00:00 2001 From: giles Date: Sat, 7 Mar 2026 22:05:39 +0000 Subject: [PATCH] Add CSSX Components plan: styling via defcomp instead of opaque style dict MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the existing CSSX plan with a component-based approach where styling is handled by regular defcomp components that apply classes, respond to data, and compose naturally — eliminating opaque hash-based class names. Co-Authored-By: Claude Opus 4.6 --- sx/sx/nav-data.sx | 4 +- sx/sx/plans.sx | 104 ++++++++++++++++++++++++++++++++++++++++--- sx/sxc/pages/docs.sx | 1 + 3 files changed, 103 insertions(+), 6 deletions(-) diff --git a/sx/sx/nav-data.sx b/sx/sx/nav-data.sx index a1758ef..beab300 100644 --- a/sx/sx/nav-data.sx +++ b/sx/sx/nav-data.sx @@ -141,7 +141,9 @@ (dict :label "Glue Decoupling" :href "/plans/glue-decoupling" :summary "Eliminate all cross-app model imports via glue service layer.") (dict :label "Social Sharing" :href "/plans/social-sharing" - :summary "OAuth-based sharing to Facebook, Instagram, Threads, Twitter/X, LinkedIn, and Mastodon."))) + :summary "OAuth-based sharing to Facebook, Instagram, Threads, Twitter/X, LinkedIn, and Mastodon.") + (dict :label "CSSX Components" :href "/plans/cssx-components" + :summary "Styling as components — replace the style dictionary with regular defcomps that apply classes, respond to data, and compose naturally."))) (define bootstrappers-nav-items (list (dict :label "Overview" :href "/bootstrappers/") diff --git a/sx/sx/plans.sx b/sx/sx/plans.sx index 2a1b892..8a646a0 100644 --- a/sx/sx/plans.sx +++ b/sx/sx/plans.sx @@ -135,7 +135,7 @@ (~doc-section :title "Context" :id "context" (p "The web is six incompatible formats duct-taped together: HTML for structure, CSS for style, JavaScript for behavior, JSON for data, server languages for backend logic, build tools for compilation. Moving anything between layers requires serialization, template languages, API contracts, and glue code. Federation (ActivityPub) adds a seventh — JSON-LD — which is inert data that every consumer must interpret from scratch and wrap in their own UI.") - (p "SX is already one evaluable format that does all six. A component definition is simultaneously structure, style (CSSX atoms), behavior (event handlers), data (the AST " (em "is") " data), server-renderable (Python evaluator), and client-renderable (JS evaluator). The pieces already exist: content-addressed DAG execution (artdag), IPFS storage with CIDs, OpenTimestamps Bitcoin anchoring, boundary-enforced sandboxing.") + (p "SX is already one evaluable format that does all six. A component definition is simultaneously structure, style (components apply classes and respond to data), behavior (event handlers), data (the AST " (em "is") " data), server-renderable (Python evaluator), and client-renderable (JS evaluator). The pieces already exist: content-addressed DAG execution (artdag), IPFS storage with CIDs, OpenTimestamps Bitcoin anchoring, boundary-enforced sandboxing.") (p "SX-Activity wires these together into a new web. Everything — content, UI components, markdown parsers, syntax highlighters, validation logic, media, processing pipelines — is the same executable format, stored on a content-addressed network, running within each participant's own security context. " (strong "The wire format is the programming language is the component system is the package manager."))) (~doc-section :title "Current State" :id "current-state" @@ -394,7 +394,7 @@ (~doc-subsection :title "The insight" (p "The web has six layers that don't talk to each other: HTML (structure), CSS (style), JavaScript (behavior), JSON (data interchange), server frameworks (backend logic), and build tools (compilation). Each has its own syntax, its own semantics, its own ecosystem. Moving data between them requires serialization, deserialization, template languages, API contracts, type coercion, and an endless parade of glue code.") - (p "SX collapses all six into one evaluable format. A component definition is simultaneously structure, style (CSSX atoms), behavior (event handlers), data (the AST is data), server-renderable (Python evaluator), and client-renderable (JS evaluator). There is no boundary between \"data\" and \"program\" — s-expressions are both.") + (p "SX collapses all six into one evaluable format. A component definition is simultaneously structure, style (components apply classes and respond to data), behavior (event handlers), data (the AST is data), server-renderable (Python evaluator), and client-renderable (JS evaluator). There is no boundary between \"data\" and \"program\" — s-expressions are both.") (p "Once that's true, " (strong "everything becomes shareable.") " Not just UI components — markdown parsers, syntax highlighters, date formatters, validation logic, layout algorithms, color systems, animation curves. Any pure function over data. All content-addressed, all on IPFS, all executable within your own security context.")) (~doc-subsection :title "What travels on the network" @@ -732,7 +732,7 @@ (li (code ":cid") " — content address of the canonical serialized source") (li (code ":deps") " — dependency CIDs, not just names. A consumer can recursively resolve the entire tree by CID without name ambiguity") (li (code ":pure") " — pre-computed purity flag. The consumer " (em "re-verifies") " this after fetching (never trust the manifest alone), but it enables fast rejection of IO-dependent components before downloading") - (li (code ":css-atoms") " — CSSX class names the component uses. The consumer can pre-resolve CSS rules without parsing the source") + (li (code ":deps") " includes style component CIDs. No separate " (code ":css-atoms") " field needed — styling is just more components") (li (code ":params") " — parameter signature for tooling, documentation, IDE support") (li (code ":author") " — who published this. AP actor URL, verifiable via HTTP Signatures"))) @@ -1570,8 +1570,7 @@ (li (strong "eval/parse/render: ") "Complete both sides. sx-ref.js has eval, parse, render-to-html, render-to-dom, aser.") (li (strong "Engine: ") "engine.sx (morph, swaps, triggers, history), orchestration.sx (fetch, events), boot.sx (hydration) — all transpiled.") (li (strong "Wire format: ") "Server _aser → SX source → client parses → renders to DOM. Boundary is clean.") - (li (strong "Component caching: ") "Hash-based localStorage for component definitions and style dictionaries.") - (li (strong "CSS on-demand: ") "CSSX resolves keywords to CSS rules, injects only used rules.") + (li (strong "Component caching: ") "Hash-based localStorage for component definitions.") (li (strong "Boundary enforcement: ") "boundary.sx + SX_BOUNDARY_STRICT=1 validates all primitives/IO/helpers at registration.") (li (strong "Dependency analysis: ") "deps.sx computes per-page component bundles — only definitions a page actually uses are sent.") (li (strong "IO detection: ") "deps.sx classifies every component as pure or IO-dependent. Server expands IO components, serializes pure ones for client.") @@ -2091,3 +2090,98 @@ (td :class "px-3 py-2 font-mono text-sm text-violet-700" "shared/sx/ref/suspense.sx") (td :class "px-3 py-2 text-stone-700" "Streaming/suspension (new)") (td :class "px-3 py-2 text-stone-600" "5")))))))) + + +;; --------------------------------------------------------------------------- +;; CSSX Components +;; --------------------------------------------------------------------------- + +(defcomp ~plan-cssx-components-content () + (~doc-page :title "CSSX Components" + + (~doc-section :title "Context" :id "context" + (p "SX currently has a parallel CSS system: a style dictionary (JSON blob of atom-to-declaration mappings), a " (code "StyleValue") " type threaded through the evaluator and renderer, content-addressed hash class names (" (code "sx-a3f2b1") "), runtime CSS injection into " (code "