Update /docs/css page with CSSX native style primitives docs
Document css primitive, style atoms, variants, defstyle, defkeyframes, and on-demand delivery protocol. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -413,45 +413,104 @@ async def _docs_primitives_sx() -> str:
|
||||
|
||||
|
||||
def _docs_css_sx() -> str:
|
||||
c1 = _code('# First page load:\n'
|
||||
'GET / HTTP/1.1\n\n'
|
||||
'HTTP/1.1 200 OK\n'
|
||||
'Content-Type: text/html\n'
|
||||
'# Full CSS in <style id="sx-css"> + hash in <meta name="sx-css-classes">\n\n'
|
||||
'# Subsequent navigation:\n'
|
||||
'GET /about HTTP/1.1\n'
|
||||
'SX-Css: a1b2c3d4\n\n'
|
||||
'HTTP/1.1 200 OK\n'
|
||||
'Content-Type: text/sx\n'
|
||||
'SX-Css-Hash: e5f6g7h8\n'
|
||||
'SX-Css-Add: bg-blue-500,text-white,rounded-lg\n'
|
||||
'# Only new rules in <style data-sx-css>', "bash")
|
||||
c_css_basic = _code(';; Native style expressions\n'
|
||||
'(div :style (css :flex :gap-4 :p-2) "hello")\n'
|
||||
';; => <div class="sx-faa0d2">hello</div>\n'
|
||||
';; with .sx-faa0d2{display:flex;gap:1rem;padding:.5rem}\n\n'
|
||||
';; Pseudo-class variants\n'
|
||||
'(button :style (css :bg-sky-500 :hover:bg-sky-600 :text-white :rounded) "Click")\n\n'
|
||||
';; Responsive variants\n'
|
||||
'(div :style (css :flex :flex-col :sm:flex-row :gap-4) ...)\n\n'
|
||||
';; Arbitrary values\n'
|
||||
'(div :style (css :w-[347px] :h-[60vh] :bg-[#f0f]) ...)')
|
||||
c_defstyle = _code(';; Named styles with defstyle\n'
|
||||
'(defstyle card-base\n'
|
||||
' (css :rounded-xl :bg-white :shadow :hover:shadow-md :transition))\n\n'
|
||||
'(div :style card-base "I am a card")\n\n'
|
||||
';; Composition with merge-styles\n'
|
||||
'(div :style (merge-styles card-base (css :p-4 :border :border-stone-200))\n'
|
||||
' "Card with extra styles")')
|
||||
c_defkeyframes = _code(';; Custom keyframes\n'
|
||||
'(defkeyframes fade-in\n'
|
||||
' (from (css :opacity-0))\n'
|
||||
' (to (css :opacity-100)))\n\n'
|
||||
'(defkeyframes slide-up\n'
|
||||
' ("0%" (css :translate-y-4 :opacity-0))\n'
|
||||
' ("100%" (css :translate-y-0 :opacity-100)))\n\n'
|
||||
';; Use with animate-{name}\n'
|
||||
'(div :style (css :animate-fade-in :duration-300) "Animated")')
|
||||
c_protocol = _code('# First page load:\n'
|
||||
'GET / HTTP/1.1\n\n'
|
||||
'HTTP/1.1 200 OK\n'
|
||||
'Content-Type: text/html\n'
|
||||
'# Full CSS in <style id="sx-css"> + hash in <meta name="sx-css-classes">\n\n'
|
||||
'# Subsequent navigation:\n'
|
||||
'GET /about HTTP/1.1\n'
|
||||
'SX-Css: a1b2c3d4\n\n'
|
||||
'HTTP/1.1 200 OK\n'
|
||||
'Content-Type: text/sx\n'
|
||||
'SX-Css-Hash: e5f6g7h8\n'
|
||||
'SX-Css-Add: bg-blue-500,text-white,rounded-lg\n'
|
||||
'# Only new rules in <style data-sx-css>', "bash")
|
||||
c_atoms = _code(';; The css primitive accepts keyword atoms\n'
|
||||
';; Each atom maps to self-contained CSS declarations:\n'
|
||||
';; :flex => display:flex\n'
|
||||
';; :gap-4 => gap:1rem\n'
|
||||
';; :bg-sky-100 => background-color:rgb(224 242 254)\n'
|
||||
';; :shadow => box-shadow:0 1px 3px 0 rgba(0,0,0,.1),...\n\n'
|
||||
';; Variant prefixes are split and wrapped automatically:\n'
|
||||
';; :hover:bg-sky-200 => .sx-xxx:hover{background-color:...}\n'
|
||||
';; :sm:flex-row => @media (min-width:640px){.sx-xxx{...}}\n'
|
||||
';; :sm:hover:bg-sky-200 => compound variant (media + pseudo)')
|
||||
return (
|
||||
f'(~doc-page :title "On-Demand CSS"'
|
||||
f'(~doc-page :title "CSS"'
|
||||
f' (~doc-section :title "Native style expressions" :id "css-primitive"'
|
||||
f' (p :class "text-stone-600"'
|
||||
f' "The css primitive replaces Tailwind class strings with native SX expressions. '
|
||||
f'Each keyword atom maps to self-contained CSS declarations. The result is a StyleValue '
|
||||
f'with a content-addressed class name (sx-{{hash}}) and generated CSS rules.")'
|
||||
f' {c_css_basic})'
|
||||
f' (~doc-section :title "Style atoms" :id "atoms"'
|
||||
f' (p :class "text-stone-600"'
|
||||
f' "~500 atoms cover all standard utilities: display, position, flexbox, grid, spacing, '
|
||||
f'sizing, typography, colors, borders, shadows, transitions, animations, and more. '
|
||||
f'Atoms use the same names as Tailwind classes but resolve to self-contained CSS — '
|
||||
f'no --tw-* custom properties needed.")'
|
||||
f' {c_atoms})'
|
||||
f' (~doc-section :title "Variants" :id "variants"'
|
||||
f' (p :class "text-stone-600"'
|
||||
f' "Pseudo-class variants: hover, focus, focus-within, focus-visible, active, disabled, '
|
||||
f'first, last, odd, even, empty, open, placeholder, group-hover, group-open. '
|
||||
f'Responsive breakpoints: sm (640px), md (768px), lg (1024px), xl (1280px), 2xl (1536px). '
|
||||
f'Variants can be combined: sm:hover:bg-sky-200."))'
|
||||
f' (~doc-section :title "Named styles" :id "defstyle"'
|
||||
f' (p :class "text-stone-600"'
|
||||
f' "defstyle binds a css expression to a name for reuse. '
|
||||
f'merge-styles combines multiple StyleValues into one, with later declarations winning.")'
|
||||
f' {c_defstyle})'
|
||||
f' (~doc-section :title "Keyframes" :id "defkeyframes"'
|
||||
f' (p :class "text-stone-600"'
|
||||
f' "defkeyframes defines custom CSS @keyframes animations. Each step takes a '
|
||||
f'selector (from, to, or a percentage) and a css body. The keyframes are registered '
|
||||
f'globally so animate-{{name}} atoms can reference them.")'
|
||||
f' {c_defkeyframes})'
|
||||
f' (~doc-section :title "On-demand delivery" :id "delivery"'
|
||||
f' (p :class "text-stone-600"'
|
||||
f' "Generated CSS rules flow through the same on-demand delivery pipeline as Tailwind classes. '
|
||||
f'On the first page load, all used CSS is embedded in a <style> block. On subsequent navigations, '
|
||||
f'the client sends a hash of known classes and the server sends only new rules.")'
|
||||
f' {c_protocol})'
|
||||
f' (~doc-section :title "How it works" :id "how"'
|
||||
f' (p :class "text-stone-600"'
|
||||
f' "sx scans every response for CSS class names used in :class attributes. '
|
||||
f'It looks up only those classes in a pre-parsed Tailwind CSS registry and ships '
|
||||
f'just the rules that are needed. No build step. No purging. No unused CSS.")'
|
||||
f' "Server-side: the css primitive resolves atoms to a StyleValue with declarations. '
|
||||
f'The HTML renderer converts :style StyleValue to a class attribute and registers the CSS rule '
|
||||
f'in the on-demand registry. Rules are delivered via the SX-Css delta protocol.")'
|
||||
f' (p :class "text-stone-600"'
|
||||
f' "On the first page load, the full set of used classes is embedded in a <style> block. '
|
||||
f'A hash of the class set is stored. On subsequent navigations, the client sends the hash '
|
||||
f'in the SX-Css header. The server computes the diff and sends only new rules via '
|
||||
f'SX-Css-Add and a <style data-sx-css> block."))'
|
||||
f' (~doc-section :title "The protocol" :id "protocol"'
|
||||
f' {c1})'
|
||||
f' (~doc-section :title "Advantages" :id "advantages"'
|
||||
f' (ul :class "space-y-2 text-stone-600"'
|
||||
f' (li "Zero build step — no Tailwind CLI, no PostCSS, no purging")'
|
||||
f' (li "Exact CSS — never ships a rule that isn\'t used on the page")'
|
||||
f' (li "Incremental — subsequent navigations only ship new rules")'
|
||||
f' (li "Component-aware — pre-scans component definitions at registration time")))'
|
||||
f' (~doc-section :title "Disadvantages" :id "disadvantages"'
|
||||
f' (ul :class "space-y-2 text-stone-600"'
|
||||
f' (li "Requires the full Tailwind CSS file loaded in memory at startup (~4MB parsed)")'
|
||||
f' (li "Regex-based class scanning — can miss dynamically constructed class names")'
|
||||
f' (li "No @apply support — classes must be used directly")'
|
||||
f' (li "Tied to Tailwind\'s utility class naming conventions"))))'
|
||||
f' "Client-side: the style dictionary is delivered as a JSON payload in a '
|
||||
f'<script type=text/sx-styles> tag, cached in localStorage with the same hash+cookie pattern '
|
||||
f'as components. The client-side css primitive mirrors the server resolver — same atoms, '
|
||||
f'same variant splitting, same content-addressed class names. Generated CSS is injected '
|
||||
f'into <style id=sx-css>.")))'
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user