Skip to content

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.

whowhat
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 ​

bash
npm install @barocss/browser
npm install -D vite tailwindcss @tailwindcss/vite

vite.config.js ​

js
import { defineConfig } from 'vite';
import tailwindcss from '@tailwindcss/vite';

export default defineConfig({
  plugins: [tailwindcss()],
});

style.css ​

css
@import "tailwindcss";

Keep this as is. BaroCSS has no CSS entry: never @import a BaroCSS package in CSS.

main.js ​

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 changes

Leave 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 ​

html
<div id="runtime-target"></div>
js
// 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)
whata 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()
fitsone runtime per page: apps, companion setups, several modules that each call it without passing an instance aroundtests, several isolated runtimes (e.g. one per Shadow DOM root), or full lifecycle control
notepass nonce/constructable on the first call; later values are ignoredtwo 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.

Released under the MIT License.