← All posts

Rux v0.3.0: fine-grained reactivity, dynamic styles, and syntax coloring

A signal change now patches only the bindings that read it, in place, instead of rebuilding the tree, which deletes the restore-pass category that caused v0.1's worst bug. Plus Vue-style :class / :style bindings and .rux syntax highlighting for VS Code and the site.

v0.2 closed with a promise: every feature it shipped needed its own restore pass: put the caret back, put the selection back, put the scroll offsets back, remember the open dropdown, remember focus. Per-binding subscriptions would delete the whole category. v0.3 is that deletion, plus two things that ride on top of it: dynamic :class / :style bindings, and real syntax coloring for .rux. 95 tests pass, and every claim below was driven in the window before it was called done.

Fine-grained reactivity

Through v0.2, any signal write rebuilt the entire tree and then restored the ephemeral state the rebuild threw away. It worked, but every new feature added one more thing to restore, and every restore pass was a chance to reproduce v0.1's nastiest bug (the caret that wouldn't leave the input you'd just left).

v0.3 replaces the rebuild with subscriptions. A binding records exactly the signals it reads, a write records exactly the signals it changed, and reflecting a change touches only the bindings that intersect. The mechanism, end to end:

The restore-pass category is gone. Set RUX_TRACE=1 and every interaction prints the path it took, either patched in place (no rebuild) or rebuilt (structural), and across the examples the wholesale rebuild simply doesn't fire anymore.

RUX_TRACE=1 cargo run -p rux-cli -- examples/form.rux

One deliberate non-deletion worth noting: apply_focus stays. It's still the mechanism that sets a caret and that scopes focus restoration after a reconcile. What's retired is its old job of restoring the whole tree on every keystroke.

:class and :style

With patching in place, dynamic attributes became cheap to add. Rux now has Vue-style bindings:

<view class="chip" r-for="c in colors" :class="c" :style="`background: ${c}`">
  <text class="chiplabel">{{ c }}</text>
</view>

The one wrinkle, documented rather than hidden: a whole-number signal renders through rhai's float default inside a backtick string (82.0, not 82). It's valid CSS, but reach for ${n.to_int()} when you want the integer.

Syntax coloring, and editor tooling

.rux files now get highlighted, from a single self-contained TextMate grammar that serves two consumers: VS Code, and the code blocks on this site (the same rux.tmLanguage.json, copied into the Zola build). One grammar, no fork, no second source of truth to drift.

The VS Code extension picked up the rest of the basics:

Labels

A <text for="…"> label is now wired to its target: tapping the label focuses a bound text input, or fires a toggle target's @tap. The role="label" accessibility half is still unbuilt.

Still missing

As plainly as the wins:

Next

The v0.3 banner is spent, so v0.4 turns to the CSS gaps that everything else waits on: custom properties, @media, and the pseudo-classes that retire the checked hack. That's the pool the roadmap draws the next Fridays from.