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>
71 lines
5.0 KiB
Plaintext
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.")))))
|