Files
rose-ash/sx/sx/applications/cssx/index.sx
giles 4f02f82f4e HS parser: fix number+comparison keyword collision, eval-hs uses hs-compile
Parser: skip unit suffix when next ident is a comparison keyword
(starts, ends, contains, matches, is, does, in, precedes, follows).
Fixes "123 starts with '12'" returning "123starts" instead of true.

eval-hs: use hs-compile directly instead of hs-to-sx-from-source with
"return " prefix, which was causing the parser to consume the comparison
as a string suffix.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 11:29:01 +00:00

71 lines
5.0 KiB
Plaintext

;; CSSX — Styling as Components
;; Documentation for the CSSX approach: no parallel style infrastructure,
;; just defcomp components that decide how to style their children.
;; ---------------------------------------------------------------------------
;; Overview
;; ---------------------------------------------------------------------------
(defcomp ()
(~docs/page :title "CSSX Components"
(~docs/section :title "The Idea" :id "idea"
(p (strong "Styling is just components.") " A CSSX component is a regular "
(code "defcomp") " that decides how to style its children. It might apply "
"Tailwind classes, or hand-written CSS classes, or inline styles, or generate "
"rules at runtime. The implementation is the component's private business. "
"The consumer just calls " (code "(~cssx/btn :variant \"primary\" \"Submit\")") " and doesn't care.")
(p "Because it's " (code "defcomp") ", you get everything for free: caching, bundling, "
"dependency scanning, server/client rendering, composition. No parallel infrastructure."))
(~docs/section :title "Why Not a Style Dictionary?" :id "why"
(p "SX previously had 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, and a separate caching pipeline (cookies, localStorage).")
(p "This was ~3,000 lines of code across the spec, bootstrappers, and host implementations. "
"It was never adopted. The codebase voted with its feet: " (code ":class") " strings "
"with " (code "defcomp") " already covered every real use case.")
(p "The result of that system: elements in the DOM got opaque class names like "
(code "class=\"sx-a3f2b1\"") ". DevTools became useless. You couldn't inspect an "
"element and understand its styling. " (strong "That was a deal breaker.")))
(~docs/section :title "Key Advantages" :id "advantages"
(ul (~tw :tokens "list-disc pl-5 space-y-2 text-stone-700")
(li (strong "Readable DOM: ") "Elements have real class names, not content-addressed "
"hashes. DevTools works.")
(li (strong "Data-driven styling: ") "Components receive data and decide styling. "
(code "(~cssx/metric :value 150)") " renders red because " (code "value > 100")
" — logic lives in the component, not a CSS preprocessor.")
(li (strong "One system: ") "No separate " (code "StyleValue") " type, no style "
"dictionary JSON, no injection pipeline. Components ARE the styling abstraction.")
(li (strong "One cache: ") "Component hash/localStorage handles everything. No "
"separate style dict caching.")
(li (strong "Composable: ") (code "(~card :elevated true (~cssx/metric :value v))")
" — styling composes like any other component.")
(li (strong "Strategy-agnostic: ") "A component can apply Tailwind classes, emit "
(code "<style>") " blocks, use inline styles, generate CSS custom properties, or "
"any combination. Swap strategies without touching call sites.")))
(~docs/section :title "What Changed" :id "changes"
(~docs/subsection :title "Removed (~3,000 lines)"
(ul (~tw :tokens "list-disc pl-5 space-y-1 text-stone-600 text-sm")
(li (code "StyleValue") " type and all plumbing (type checks in eval, render, serialize)")
(li (code "cssx.sx") " spec module (resolve-style, resolve-atom, split-variant, hash, injection)")
(li "Style dictionary JSON format, loading, caching (" (code "<script type=\"text/sx-styles\">") ", localStorage)")
(li (code "style_dict.py") " (782 lines) and " (code "style_resolver.py") " (254 lines)")
(li (code "css") " and " (code "merge-styles") " primitives")
(li "Platform interface: " (code "fnv1a-hash") ", " (code "compile-regex") ", " (code "make-style-value") ", " (code "inject-style-value"))
(li (code "defkeyframes") " special form")
(li "Style dict cookies and localStorage keys")))
(~docs/subsection :title "Kept"
(ul (~tw :tokens "list-disc pl-5 space-y-1 text-stone-600 text-sm")
(li (code "defstyle") " — simplified to bind any value (string, function, etc.)")
(li (code "tw.css") " — the compiled Tailwind stylesheet, delivered via CSS class tracking")
(li (code ":class") " attribute — just takes strings, no special-casing")
(li "CSS class delivery (" (code "SX-Css") " headers, " (code "<style id=\"sx-css\">") ")")
(li "All component infrastructure (defcomp, caching, bundling, deps)")))
(~docs/subsection :title "Added"
(p "Nothing. CSSX components are just " (code "defcomp") ". The only new thing is "
"a convention: components whose primary purpose is styling.")))))