astryx-svelte v0.3.1: the setup step is gone
Install, import two stylesheets, done. Plus the RTL bug a new stylesheet oracle found on its first run, and the cascade defect hiding in four documented recipes.0.3.0 shipped with a setup step nobody should have had to perform. Installing the package was not
enough: your bundler also had to run the StyleX compiler, and it had to run it with the same
options this package's own build uses. Get that wrong and nothing failed. No error, no warning — the
components rendered, with correct markup and no styling at all.
0.3.1 deletes the step.
tsimport { Button } from '@astryx-svelte/core';import '@astryx-svelte/core/base.css';import '@astryx-svelte/core/astryx.css';
That is the whole setup now.
The fix was a missing port, not a new feature
The 0.3.0 release notes explained the setup step as intrinsic: "Astryx's own package ships pre-built
CSS; this package cannot." That sentence was wrong, and it was wrong in the most ordinary way — by
never checking.
Upstream publishes
@astryxdesign/core/astryx.css, generated by a post-build script in their repo.
We had simply not ported that script. So the "limitation" was an omission wearing a justification,
and the fix was to do what upstream does: compile the styles at publish time and ship the stylesheet.Porting it settled a question that had been open for months.
useCSSLayers had been on the backlog
as "align with upstream's form", and reading their generator answered it: they call
processStylexRules(rules, false) and wrap the result in a single @layer astryx-base. Priority is
expressed as :not(#\#) specificity padding, not as @layer priority1…9. So a consumer ordering
layers around Astryx orders around one layer name — and it has to be upstream's name, or CSS
written for Astryx does not transfer.Shipping the stylesheet was not enough
The first version of this change published
astryx.css and stopped there. It did not work, and the
way it failed is worth recording.dist shipped uncompiled — svelte-package transpiles TypeScript and does not run StyleX — so the
published modules still contained literal stylex.create calls. The assumption was that those would
degrade to a runtime no-op and the stylesheet would supply the classes.They do not degrade. They throw:
Unexpected 'stylex.create' call at runtime. Styles must be compiled by '@stylexjs/babel-plugin'.
So the stylesheet by itself produced a crash rather than an unstyled page.
prepack now compiles
dist/**/*.stylex.js as well. Only create, defineVars and keyframes are compile-time —
props is a genuine runtime function, and a .svelte file reaches StyleX only through it — so
compiling the 200 style modules is enough to make the whole package run with no compiler present.That compile step checks itself, and the check earned its place immediately: the first run reported
26 classes that
dist referenced but the stylesheet did not contain. StyleX derives defineVars
companion classes from a module's path, and the two builds were compiling the same styles from
different locations — src/lib/… for the stylesheet, dist/… for the modules. Compiling each module
under its source identity closed the gap. The assertion stays, so that class of drift cannot ship
quietly.Verified the way a user would experience it: a Vite app with no StyleX plugin at all, built and
driven in headless Chromium, renders an Avatar at
border-radius: 9999px, 36×36, inline-flex.A third oracle, and the bug it found
The port has always had two oracles diffing our compiled output against upstream's published
packages. Both read
.stylex.ts modules statically, which means neither can see inside a
stylex.create function style — a style that takes an argument. There are 54 of them, and that
blindness was documented but uncovered.The new stylesheet gave us a way to cover it.
compare-upstream-css.mjs diffs the generated
astryx.css against upstream's published one, and a function style's output is just another rule
there. Result: 1,463 shared atomic classes, zero differing rules.It found a real bug on its first run.
Avatar's status dot — the little presence indicator — was positioned with a physical
right,
matching upstream 0.2.0. Upstream 0.3.0 had moved it to insetInlineEnd and mirrored the
transform that pushes it onto the circle's edge:tstransform: {default: 'translate(50%, 50%)',':is([dir="rtl"] *)': 'translate(-50%, 50%)'}
Ours had kept the old version, under a comment arguing for it. In right-to-left layouts the dot sat
on the wrong side. The class oracle had reported zero mismatches the entire time, correctly — the
bug was in a function style, where it could not look.
The cascade defect nobody could see
base.css declared four layers: reset, astryx-base, astryx-theme, product.But compiling this package yourself emits nine —
@layer priority1 through priority9 — and CSS
layer order is order of first appearance. Nine layers nobody had named therefore sorted after
product, inverting the whole cascade. Themes stopped being able to override components. Your app's
CSS lost to component CSS it should have beaten. Silently, in every case.The complete order had existed for months. It lived in this site's own
app.html, where no consumer
could benefit from it. base.css now names priority1…16 in the right position, verified in a real
build rather than reasoned about: the declaration lands at byte 13,394, ahead of astryx-theme at
13,580.Also in this release
- Every package declares
main. Withexportspresent, Node and modern bundlers never read it, which is exactly why nobody noticed — but anything that does not understandexportscould not resolve the package at all. - A
sourceexport condition on all nine non-CSS subpaths, so you can still compile from source and tree-shake if you would rather ship less CSS than the full 131 kB. -
doctoraccepts both routes. It gained a StyleX-wiring check in this release, and without this it would have told every correctly configured project that its components render unstyled. - Fixed:
doctorreported optional peer dependencies as missing, telling projects on other bundlers to install two packages they have no use for.
Upgrading, and the one thing to watch
This is a delivery change, and a silent one if you miss it. A pre-compiled
dist gives your StyleX
plugin nothing to compile — so if you configured the compiler for 0.3.0, you will now get no
component CSS.Either add the stylesheet import:
tsimport '@astryx-svelte/core/astryx.css';
or keep compiling and ask for the new condition:
ts// vite.config.tsexport default defineConfig({plugins: [astryx(), sveltekit()],resolve: { conditions: ['source'] }});
This site was the first thing to hit it. Its CSS fell from ~250 kB to 161 kB with no error at all,
which is a fair demonstration of why the failure mode is worth naming twice.
Release StyleX Parity
