Vite + Tailwind 4
Tailwind builds every class it finds in your sources at build time. Classes that arrive later (HTML from an API, a CMS, a model's output, user themes) are not in the build. BaroCSS runs in the browser next to the build and generates only those missing classes.
| who | what |
|---|---|
Tailwind (@tailwindcss/vite) | classes in your source files, at build time |
BaroCSS (@barocss/browser) | classes that appear in the DOM after the build, at runtime (skipExisting: true skips the ones the build already has) |
Install
npm install @barocss/browser
npm install -D vite tailwindcss @tailwindcss/vitevite.config.js
import { defineConfig } from 'vite';
import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
plugins: [tailwindcss()],
});style.css
@import "tailwindcss";Keep this as is. BaroCSS has no CSS entry: never @import a BaroCSS package in CSS.
main.js
import './style.css';
import { getRuntime } from '@barocss/browser';
const runtime = getRuntime({
skipExisting: true, // only generate classes the Tailwind build does not define
config: {
cssVarPrefix: 'tw', // share --tw-* composite variables with the build
darkMode: 'class', // only if your CSS has @custom-variant dark (&:is(.dark *))
darkModeSelector: '.dark &',
},
});
runtime.observe(document.body, { scan: true }); // scans now and watches later DOM changesLeave out darkMode/darkModeSelector if your CSS has no @custom-variant dark (dark mode then follows the OS setting, as in the build). For theme, @utility and prefix(tw) settings see Next to a Tailwind build.
Runtime insert example
<div id="runtime-target"></div>// Classes not in any source file, so Tailwind never built them:
const html = '<div class="bg-emerald-700 ring-4 ring-emerald-300 p-6 rounded-lg">Runtime inserted box</div>';
document.getElementById('runtime-target').innerHTML = html;bg-emerald-700 and ring-* are generated by BaroCSS when the element lands; p-6/rounded-lg are reused from the build if it already has them. Check in DevTools: runtime.getCss('bg-emerald-700') returns a CSS string.
This flow is covered by packages/barocss-browser/tests/vite-companion-420.test.ts (pnpm --filter @barocss/browser exec vitest run tests/vite-companion-420.test.ts).
BrowserRuntime or getRuntime?
Both give you the same runtime; they differ in who owns the instance.
getRuntime(options) | new BrowserRuntime(options) | |
|---|---|---|
| what | a page-wide singleton: the first call creates it, later calls return the same one (a new config is applied with updateConfig) | an explicit instance you create, keep and destroy() |
| fits | one runtime per page: apps, companion setups, several modules that each call it without passing an instance around | tests, several isolated runtimes (e.g. one per Shadow DOM root), or full lifecycle control |
| note | pass nonce/constructable on the first call; later values are ignored | two instances on the same document each inject styles; avoid that |
baroBoot() / baroStart() build on getRuntime: they create the singleton, scan the body and start observing in one call; with root: shadowRoot they return a separate BrowserRuntime for that root (see Quick Start). Method reference: Browser Runtime.