← All posts

Rux v0.2.0 — selection, scrollbars, and a lot of CSS

Text selection and the system clipboard, select and textarea, keyboard focus, real scrollbars in both axes, and the CSS push: fonts, gradients, shadows, transforms, per-corner radius, grid placement.

v0.1 could open a window and edit a field. v0.2 is about making that field feel like a field — and about closing the gap between "valid CSS" and "CSS that does something."

Four things landed: text selection + clipboard, the last two input types, scrolling polish, and a serious CSS push. 74 tests pass, and every item below was driven in the window before it was called done.

Text selection and the clipboard

A focused input now has a selection, not just a caret.

The modelling decision that made this clean: a selection isn't a second thing to track alongside the caret, it's the range between a caret and an anchor. So focus became { model, caret, anchor }, and the one restore pass that puts the caret back after a rebuild puts the selection back too. Given that v0.1's nastiest bug was a restore pass that only handled half its job, not adding a second one mattered.

The trap worth writing down: parley will hand you selection rectangles, and they're laid out on parley's line pitch. Rux draws lines with the leading trimmed, which is a different pitch. Take those rects as-is and the highlight drifts a little further off the glyphs with every wrapped line. It's invisible in a one-line field, obvious the moment you drag through a textarea. So Rux takes only the horizontal extent from parley and recomputes the vertical from its own line stepping, keyed by the line index parley helpfully returns beside each rect.

select, textarea, and keyboard focus

Two fixes came straight out of using it: inputs were hugging their text and shrinking as you typed (they now default to width: 100%), and single-line inputs wrapped when they should clip.

Scrolling that behaves like scrolling

v0.1's scrolling was wheel-only and vertical-only. Now:

The bug in this batch is a good advertisement for looking at things. The horizontal thumb was painted using the track's length as its thickness: a pale slab across the entire box, impossible to miss on screen, and completely invisible to a test suite that only ever checked the vertical bar. The axis you didn't test is the axis that's broken.

The CSS push

The biggest gap in v0.1 wasn't a missing feature, it was silence: you wrote valid CSS, nothing happened, and nothing told you why. v0.2 attacks that from both ends: more honored properties, and a warning when a declaration is ignored.

Newly honored: font-family (you could not choose a font at all before, the single most visible gap), font-style, letter-spacing, word-spacing, line-height, text-decoration, white-space: nowrap, box-shadow, linear and radial gradients, background-image: url(), transform (translate/scale/rotate), per-corner border-radius, grid-column / grid-row / grid-auto-*, aspect-ratio, position + inset, align-self / justify-self / align-content, row-gap / column-gap, and cursor: pointer.

And two silent-wrongness bugs, which are worse than gaps:

transform came with a caveat that's documented rather than hidden: hit, focus and scroll regions are computed untransformed, so a transformed element still taps at its original position.

Still missing

Written as plainly as the wins:

Next: fine-grained reactivity

v0.2 shipped four features that each needed their own restore pass: put the caret back, put the selection back, put the scroll offsets back, remember which dropdown was open, remember where focus was. That list is now six entries long, and every entry is a chance to reproduce v0.1's caret bug.

Per-binding subscriptions delete the entire category. That's v0.3, and it's the last real divergence between the architecture doc and the code.