Metadata-Version: 2.5
Name: stixis
Version: 0.1.0
Summary: Spec-driven CSS serializer and validator generated from W3C @webref/css and Web Platform Tests
Project-URL: Homepage, https://github.com/myakura/stixis
Project-URL: Repository, https://github.com/myakura/stixis.git
Project-URL: Issues, https://github.com/myakura/stixis/issues
Author-email: Masataka Yakura <myakura.web@gmail.com>
License-Expression: MIT
License-File: LICENSE
Keywords: css,cssom,font-face,pep-750,serializer,validator,webref,wpt
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Code Generators
Classifier: Topic :: Text Processing :: Markup
Classifier: Typing :: Typed
Requires-Python: >=3.14
Description-Content-Type: text/markdown

# Stixis

**Stixis** (`stixis`) is a spec-driven, zero-dependency CSS serializer and validator for Python 3.14+ that generates its grammar tables and `TypedDict` definitions from W3C `@webref/css` (`ed/css/*.json`) and its conformance test suites from Web Platform Tests (WPT) and Hypothesis.

## Highlights

* **100% `@webref/css` property & descriptor coverage**: Compiles W3C Value Definition Syntax (`|`, `||`, `&&`, juxtaposition, `?`, `*`, `+`, `#`, `{A,B}`, `[...]!`, `<type [min,max]>`, `<'prop'>`) into a dual-mode `ValueNode` evaluator that serializes structured Python objects (`serialize_value`) and canonicalizes raw CSS token streams (`canonicalize`).
* **Eagerly validated immutable AST nodes**: `FontFace`, `StyleRule`, `AtRule`, `StyleSheet`, `CommentNode`, and `RawDeclaration` (`dataclass(frozen=True, slots=True)`) validate selectors, preludes, descriptors, and declarations inside `__post_init__`.
* **First-class Python 3.14+ PEP 750 `Template` (`t"..."`) support**: Separates static CSS syntax from dynamic `Interpolation` slots, applying context-aware escaping inside comments (`/* ... */`), strings, `url(...)`, and numeric slots before validating the resulting token stream against the `ValueNode` grammar.
* **Five built-in CSS microsyntaxes**:
  1. `<urange>` (`parse_urange`, `format_urange`, and `bisect`-based cascade-shadowing range compaction `compact_urange`).
  2. `<opentype-tag>` (`font-feature-settings`, `font-variation-settings`, `font-language-override`).
  3. `<font-src-list>` (`FontSrc`, `Url`, `LocalFont` with `format()` -> `tech()` ordering and keyword validation).
  4. `<family-name>` & `<generic-family>` (safe default quoting of Python `str` family names, explicit `generic("serif")` / `Keyword("serif")`, and WPT `font-family-serialization-001.html` conformance).
  5. `{1,2}` descriptor and property ranges (`font-weight`, `font-width` / `font-stretch`, `font-style: oblique`, metric overrides).
* **Agent-first read-only CLI**: `stixis inspect` and `stixis check` with deterministic exit codes (`0` valid, `1` invalid value, `2` usage error, `4` unknown target).

## Quickstart

```python
from stixis import (
    Deg,
    FontFace,
    FontSrc,
    Pct,
    Px,
    SerializationOptions,
    StyleRule,
    StyleSheet,
    compact_urange,
    dumps,
    generic,
)

if __name__ == "__main__":
    woff2_url = "https://example.com/fonts/noto-sans-jp-slice0.woff2"
    slice_id = "kana"
    size_kb = 18.4

    urange = compact_urange(
        sorted_target_cps=[0x3000, 0x3041, 0x3042, 0x3044],
        forbidden_sorted=[0x3043],
        max_gap=0x80,
    )

    sheet = StyleSheet(
        header_comment="Generated by Stixis",
        rules=[
            FontFace(
                comment=t"Slice [{slice_id}] ({size_kb:.1f} KB)",
                font_family="Noto Sans JP",
                font_style=("oblique", Deg(-10), Deg(20)),
                font_weight=(100, 900),
                font_width=(Pct(75), Pct(125)),
                font_display="swap",
                src=t"url({woff2_url}) format(woff2) tech(variations)",
                unicode_range=urange,
            ),
            StyleRule(
                "body",
                {
                    "font_family": ["Noto Sans JP", generic("sans-serif")],
                    "font_feature_settings": {"liga": True, "dlig": False},
                    "margin": Px(0),
                },
            ),
        ],
    )

    print(dumps(sheet))
    print(dumps(sheet, options=SerializationOptions.cleome()))
```

## CLI Usage

```bash
# Inspect an at-rule, property, or <type>
uv run stixis inspect @font-face
uv run stixis inspect font-style --json
uv run stixis inspect "<font-src>"

# Validate and canonicalize CSS values
uv run stixis check font-weight "100 900" --at-rule @font-face
uv run stixis check unicode-range "u+3040-309f" --at-rule @font-face
```

## Development & Verification

```bash
uv sync
uv run python codegen/gen_specs.py --check
uv run python codegen/gen_wpt_tests.py --check
uv run pytest
uv run ruff check
```
