Styling a Janux app#
Janux has no opinion about how you write CSS, and no vite.config for you to edit. What it does have is a single, predictable contract: one stylesheet entry, one <link> in the HTML shell. Everything below is a different way of filling that entry.
| Approach | Resolved | Use it when |
|---|---|---|
| Global styles | build | The baseline: plain CSS in src/styles.css. Everything else builds on it. |
| Inline and JSX styles | render | A value only the component knows — a computed width, a colour from data. |
| CSS variables | runtime | Theming that changes while the page is live, without shipping new rules. |
| Dark mode | runtime | Following (or overriding) the reader's OS preference. |
| Sass | build | Variables, nesting, mixins and loops that generate CSS for you. |
| Tailwind | build | Utility classes, with the whole setup being one dependency. |
The one contract#
Whatever you choose, the app's stylesheet is the file named src/styles.<ext>:
src/
styles.css ← or styles.scss, styles.sass, styles.lessjanux dev serves it through Vite (with hot reload); janux build compiles it and emits dist/client/styles.css — the one sheet the server-rendered shell links, whatever extension you started from. There is nothing to register and nothing to import: the file's existence is the configuration.
<link rel="stylesheet" id="jx-style-0" href="/styles.css">Choosing between them#
These are not exclusive, and most real apps use two or three at once — the with-sass example uses a preprocessor for its tokens and custom properties for the parts that change at runtime.
A rough guide:
- Reach for global CSS first. A Janux page is server-rendered HTML with real class names; plain CSS goes further here than in a client-rendered framework.
- Reach for a preprocessor when you find yourself writing the same block with four different colours — that is what
@eachis for. - Reach for custom properties when the value has to change after the build: a theme picker, a density toggle, a brand colour that arrives from an API.
- Reach for inline styles only for values the stylesheet genuinely cannot know.
What is not supported yet#
CSS Modules (import styles from './X.module.css') do not work in production builds. Vite scopes the class names for the client bundle, but the server renderer imports the same file through Bun, which hands back a URL rather than a class map — so server-rendered markup gets an empty class attribute, and the emitted module CSS is never linked by the shell. Use CSS variables or a naming convention (BEM and friends) for component-scoped styling in the meantime.