@praxisjs/css
Changelog for @praxisjs/css — scoped CSS decorators and reactive CSS properties for PraxisJS.
@praxisjs/css
0.3.1
Fixed build-time static extraction (praxisjsCSS()) silently dropping all CSS for any Stylesheet that read values through tokenVars(). The extraction sandbox stubbed TokenSheet as a plain empty class instead of the real Proxy-based implementation, so static property access on a token subclass (e.g. t.colorPrimary) resolved to undefined instead of 'var(--color-primary)' — and since that undefined was often used inside a template literal or property value, the sheet's constructor could throw and get silently skipped by extraction's error handling, leaving the component completely unstyled in production builds. TokenSheet and tokenVars are now the real implementations during extraction, same as Stylesheet.
0.3.0
globalStyle() — layer option
globalStyle() now accepts a layer option to wrap the injected CSS in a named @layer, so it can be ordered against other layered CSS (like Tailwind's @layer utilities) instead of always winning the cascade regardless of specificity. preflight() is now implemented on top of this option. See Layering.
Docs correction: layer ordering across dev and production builds
The initial guidance for combining preflight()/globalStyle() with Tailwind (call it before importing Tailwind's CSS) only holds in dev, where both register their @layer via JS-injected <style> tags in import order. In production, Tailwind's CSS is typically extracted into a static <link> parsed before any JS runs, which flips the effective order and silently drops Tailwind utility classes. Coexisting with Tailwind now documents the robust fix: declaring a bare @layer reset; up front in your own static CSS, before Tailwind's @import.
0.2.0
preflight() — layered by default for Tailwind coexistence
preflight() now wraps its reset in @layer reset by default, so it can be ordered against other layered CSS (like Tailwind's @layer utilities) instead of always winning the cascade regardless of specificity. Pass preflight({ layer: 'custom-name' }) to rename the layer, or preflight({ layer: false }) to opt back into the previous un-layered behavior. See Coexisting with Tailwind.
0.1.5
Internal: style decorators read component anchors through core helpers.
@Style and @Styled now locate component containers through @praxisjs/core/internal anchor helpers instead of reading _anchor from component instances. Runtime behavior is unchanged.
0.1.0
Initial release.
Stylesheet and ReactiveStylesheet — base classes
Extend Stylesheet to define $-prefixed class fields using the fluent this.css({}) builder or raw CSS strings. Extend ReactiveStylesheet when you also need @Param() reactive CSS custom properties.
@Styled(StyleClass) — scoped class name injection
Field decorator that processes a Stylesheet subclass and injects a typed class name map into the component. Each $-prefixed field becomes a globally-unique, content-hashed class name (e.g. prx-root-a1b2c3). Works on any component; ReactiveStylesheet subclasses are restricted to StatefulComponent at the TypeScript level.
this.css({}) — fluent CSS builder
Typed CSS builder backed by csstype. Chains pseudo-classes, pseudo-elements, and at-rules:
.hover(), .focus(), .focusVisible(), .disabled(), .media(), .container(), .supports(), .on(), and more.
@Param() — reactive CSS vars inside a stylesheet
Marks a ReactiveStylesheet field as a reactive CSS custom property. Setting this.$card.color = '#ef4444' calls element.style.setProperty('--color', '#ef4444') with no re-render.
@Style('--var') — reactive CSS vars on the component
Field decorator that binds a signal value directly to a CSS custom property on the component's container element. Updates in the same tick as the signal.
keyframes(name, stops) — scoped animations
Defines a @keyframes block and injects it once into document.head. Name is content-hashed to prevent collisions. Returns the scoped name for use in animation properties.
globalStyle(factory) — unscoped global CSS
Injects unscoped CSS exactly once (content-hashed). Receives a factory function passed css (= createCSSBuilder, identical to this.css() in Stylesheet) that returns a CSSBuilder or raw CSS string. Use .on(selector, props) for element-level rules or return a raw string for @font-face, layers, and other at-rules. Routed through the Vite plugin for static extraction.
cx(...args) — class name composition
Composes class names from strings, objects, and arrays. Falsy values are filtered. Designed to work with @Styled class name references.
Design token system — TokenSheet, ThemeInstance, @Themed, @Theme, theme()
TokenSheet— base class for token skeletons and themes. Static property access returns CSS var references:AppTokens.colorPrimary→'var(--color-primary)'.tokenVars(cls)— typed accessor for CSS var refs for use inStylesheetfield definitions.ThemeInstance— manages the active theme: injects:rootCSS vars, switches with.switch(ThemeClass), persists tolocalStorage, syncs across tabs viaBroadcastChannel.@Themed(skeleton, DefaultTheme, config?)— class decorator on the root component that creates the singletonThemeInstance. Accepts{ persist, syncTabs }config.@Theme()— field decorator that injects the singletonThemeInstance.theme()— imperative accessor for the singletonThemeInstance.
Static extraction via praxisjsCSS() Vite plugin
CSS rules are extracted at build time and emitted as virtual:praxisjs/styles.css. No <style> elements are injected at runtime in production.
praxisjsCSS()— Vite plugin exported from@praxisjs/vite-plugin. Scans source files, evaluates them in a sandboxed Node.js context, and emits the combined CSS as a static asset.- In development, the virtual module is empty and CSS is injected at runtime as usual. HMR works without extra configuration.
@praxisjs/css/server — SSR collector API
createCollector(), setCollector(), getCollector() — route CSS through a memory buffer instead of the DOM, for server-side rendering.
@praxisjs/css/extract — build-time extraction API
extractionModule(emit) — low-level factory for build-time CSS collection without the Vite plugin, for framework integrators.
preflight() — browser reset
Injects an opinionated browser reset: normalises box-sizing, removes default margins and padding, resets borders, makes replaced elements block-level, and sets sensible form and typography defaults. Inspired by the Tailwind CSS preflight — adapted to use standard system font stacks with no Tailwind-specific references. Idempotent and compatible with static extraction via praxisjsCSS().