Vext JSCSS
Table of Contents
- When to use JSCSS
- Build your first component style
- How extraction reaches the browser
- Common styling tasks
- CSS variables: build-time declarations and browser changes
- Configuration choices
- Troubleshooting
When to use JSCSS
Vext JSCSS turns a TypeScript object into a generated CSS class at build time. Use it when a component needs named variants, semantic CSS variables, or nested rules while you still want the final browser to load CSS rather than a CSS-in-JS runtime.
Choose the smallest tool that fits the job:
Vext does not compile Sass or SCSS source files. If a team keeps Sass, compile it to CSS before Vext sees it. JSCSS is not a Sass replacement; it is the built-in path for typed, component-level generated CSS.
Build your first component style
This is the recommended first path: define a named recipe in a *.style.ts file, then call the recipe from React's className.
1. Define the button recipe
Create src/frontend/styles/button.style.ts.
recipe() accepts rule objects in base and variants. style() already returns a class-name string, so do not write base: style({ ... }) or primary: style({ ... }) inside a recipe. Give the recipe a name so generated classes are recognizable when you inspect HTML or CSS.
2. Use the recipe in a React component
Create src/frontend/components/Button.tsx.
button({ intent: "primary" }) returns the base class plus the matching variant class. The default variant means button() also produces a primary button when no selection is supplied.
3. Render it from a page
How extraction reaches the browser
Run the normal production build:
Vext discovers matching *.style.ts, *.style.js, and *.css.ts files under src/frontend/**, evaluates their declarations during the build, and writes the collected rules to generated JSCSS CSS. The generated browser entry references that CSS, and the final client asset manifest carries it into the rendered document.
You do not import an Emotion or styled-components runtime for this path. The className returned by style() or recipe() is the bridge from React to extracted CSS.
Keep a *.style.ts module declarative: it runs during a Node build step, so do not read window, document, request data, or server-only services at module scope.
Common styling tasks
Make one named class
Use style() when a component only needs one class.
Numbers become pixel values where CSS expects a length. Unitless properties such as opacity, zIndex, and fontWeight stay unitless.
Add hover and media rules
Nested selectors use &; at-rules stay inside the same object.
Choose a variant at render time
Use a recipe for a finite set of visual choices. Keep selection names meaningful to the component (intent, size, state) rather than mirroring raw CSS values.
CSS variables: build-time declarations and browser changes
createVar() creates a semantic CSS custom-property reference. setVar() returns an object that can be placed in a JSCSS rule; it does not mutate the browser document by itself.
The example above emits an initial declaration and var(--vext-accent, #4f46e5) in extracted CSS. For a value that must change after hydration, use the normal browser CSS API from an event handler or effect—not from a style module or SSR render:
Configuration choices
JSCSS is enabled by default. Only change its settings when you have a specific delivery constraint:
See Frontend Configuration for the complete field reference and defaults.
Troubleshooting
Next: compare Styles and Assets for the other supported styling paths, or read Frontend Configuration when you need to tune JSCSS extraction.