Implement CSSX Phase 2: native SX style primitives
Some checks failed
Build and Deploy / build-and-deploy (push) Has been cancelled
Some checks failed
Build and Deploy / build-and-deploy (push) Has been cancelled
Replace Tailwind class strings with native SX expressions: (css :flex :gap-4 :hover:bg-sky-200) instead of :class "flex gap-4 ..." - Add style_dict.py: 516 atoms, variants, breakpoints, keyframes, patterns - Add style_resolver.py: memoized resolver with variant splitting - Add StyleValue type to types.py (frozen dataclass with class_name, declarations, etc.) - Add css and merge-styles primitives to primitives.py - Add defstyle and defkeyframes special forms to evaluator.py and async_eval.py - Integrate StyleValue into html.py and async_eval.py render paths - Add register_generated_rule() to css_registry.py, fix media query selector - Add style dict JSON delivery with localStorage caching to helpers.py - Add client-side css primitive, resolver, and style injection to sx.js Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -611,6 +611,7 @@ details.group{{overflow:hidden}}details.group>summary{{list-style:none}}details.
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-stone-50 text-stone-900">
|
||||
<script type="text/sx-styles" data-hash="{styles_hash}">{styles_json}</script>
|
||||
<script type="text/sx" data-components data-hash="{component_hash}">{component_defs}</script>
|
||||
<script type="text/sx" data-mount="body">{page_sx}</script>
|
||||
<script src="{asset_url}/scripts/sx.js?v={sx_js_hash}"></script>
|
||||
@@ -681,6 +682,14 @@ def sx_page(ctx: dict, page_sx: str, *,
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Style dictionary for client-side css primitive
|
||||
styles_hash = _get_style_dict_hash()
|
||||
client_styles_hash = _get_sx_styles_cookie()
|
||||
if client_styles_hash and client_styles_hash == styles_hash:
|
||||
styles_json = "" # Client has cached version
|
||||
else:
|
||||
styles_json = _build_style_dict_json()
|
||||
|
||||
return _SX_PAGE_TEMPLATE.format(
|
||||
title=_html_escape(title),
|
||||
asset_url=asset_url,
|
||||
@@ -688,6 +697,8 @@ def sx_page(ctx: dict, page_sx: str, *,
|
||||
csrf=_html_escape(csrf),
|
||||
component_hash=component_hash,
|
||||
component_defs=component_defs,
|
||||
styles_hash=styles_hash,
|
||||
styles_json=styles_json,
|
||||
page_sx=page_sx,
|
||||
sx_css=sx_css,
|
||||
sx_css_classes=sx_css_classes,
|
||||
@@ -697,6 +708,58 @@ def sx_page(ctx: dict, page_sx: str, *,
|
||||
|
||||
|
||||
_SCRIPT_HASH_CACHE: dict[str, str] = {}
|
||||
_STYLE_DICT_JSON: str = ""
|
||||
_STYLE_DICT_HASH: str = ""
|
||||
|
||||
|
||||
def _build_style_dict_json() -> str:
|
||||
"""Build compact JSON style dictionary for client-side css primitive."""
|
||||
global _STYLE_DICT_JSON, _STYLE_DICT_HASH
|
||||
if _STYLE_DICT_JSON:
|
||||
return _STYLE_DICT_JSON
|
||||
|
||||
import json
|
||||
from .style_dict import (
|
||||
STYLE_ATOMS, PSEUDO_VARIANTS, RESPONSIVE_BREAKPOINTS,
|
||||
KEYFRAMES, ARBITRARY_PATTERNS, CHILD_SELECTOR_ATOMS,
|
||||
)
|
||||
|
||||
# Derive child selector prefixes from CHILD_SELECTOR_ATOMS
|
||||
prefixes = set()
|
||||
for atom in CHILD_SELECTOR_ATOMS:
|
||||
# "space-y-4" → "space-y-", "divide-y" → "divide-"
|
||||
for sep in ("space-x-", "space-y-", "divide-x", "divide-y"):
|
||||
if atom.startswith(sep):
|
||||
prefixes.add(sep)
|
||||
break
|
||||
|
||||
data = {
|
||||
"a": STYLE_ATOMS,
|
||||
"v": PSEUDO_VARIANTS,
|
||||
"b": RESPONSIVE_BREAKPOINTS,
|
||||
"k": KEYFRAMES,
|
||||
"p": ARBITRARY_PATTERNS,
|
||||
"c": sorted(prefixes),
|
||||
}
|
||||
_STYLE_DICT_JSON = json.dumps(data, separators=(",", ":"))
|
||||
_STYLE_DICT_HASH = hashlib.md5(_STYLE_DICT_JSON.encode()).hexdigest()[:8]
|
||||
return _STYLE_DICT_JSON
|
||||
|
||||
|
||||
def _get_style_dict_hash() -> str:
|
||||
"""Get the hash of the style dictionary JSON."""
|
||||
if not _STYLE_DICT_HASH:
|
||||
_build_style_dict_json()
|
||||
return _STYLE_DICT_HASH
|
||||
|
||||
|
||||
def _get_sx_styles_cookie() -> str:
|
||||
"""Read the sx-styles-hash cookie from the current request."""
|
||||
try:
|
||||
from quart import request
|
||||
return request.cookies.get("sx-styles-hash", "")
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
|
||||
def _script_hash(filename: str) -> str:
|
||||
|
||||
Reference in New Issue
Block a user