tailwindlabs/tailwindcss
@tailwindcss/browser
Active contributors: Robin, Philipp, Jordan
Purpose
A self-contained Tailwind build that runs entirely in the browser. Drop a <script> tag, write <style type="text/tailwindcss">…</style> blocks, and the bundle compiles them on the fly while watching the DOM for changes. The bundle ships as a single global IIFE (./dist/index.global.js).
It is the only Node-free production consumer of the compiler — the tailwindcss package's zero-runtime-dependency design exists in part for this package.
Directory layout
packages/@tailwindcss-browser/
├── package.json # main: ./dist/index.global.js
├── README.md
├── tsconfig.json
├── tsup.config.ts # output: iife, minify
├── playwright.config.ts # UI tests
├── src/
│ ├── index.ts # The browser orchestrator (~7,000 bytes)
│ ├── assets.ts # Inlines preflight.css, theme.css, utilities.css, index.css
│ ├── instrumentation.ts # Performance API instrumentation
│ └── types.d.ts
└── tests/
└── (Playwright spec files)How it works
graph TD
Boot[Page load] --> Init[index.ts top-level]
Init -->|find STYLE_TYPE styles| Read[Read all style[type='text/tailwindcss'] tags]
Read -->|inject @import 'tailwindcss' if missing| Compile[tailwindcss.compile]
Compile --> Build[Build CSS from collected classes]
Build --> Inject[<style> output]
Boot --> Observe[MutationObserver on documentElement]
Observe -->|class change| Incr[Incremental rebuild]
Observe -->|new <style type='text/tailwindcss'>| Full[Full rebuild + recompile]
Full --> Compile
Incr --> BuildTwo observers run continuously:
- A
MutationObserverondocument.documentElementwatching:attributeFilter: ['class']— re-collect classes from changed elements (incremental rebuild).childList: true+subtree: true— detect new<style type="text/tailwindcss">tags (full rebuild).
- A second
MutationObserveron each individual<style>block watching for content changes (characterData,subtree).
Whenever a rebuild is queued, the work is serialized through a buildQueue Promise so concurrent rebuilds collapse safely.
Asset loading
Because there's no filesystem, the browser build inlines the Tailwind asset CSS files at bundle time. assets.ts exports assets.css.index, assets.css.preflight, assets.css.theme, and assets.css.utilities as string constants. The loadStylesheet callback handed to tailwindcss.compile returns these strings for the canonical asset IDs (tailwindcss, tailwindcss/preflight, ./preflight.css, etc.).
loadModule always throws — plugins and JS configs are not supported in the browser bundle.
What's missing vs. the Node adapters
- No file scanning. Classes come from
document.querySelectorAll('[class]'), not the Rust scanner. - No
@plugin, no@config.loadModulerejects the call. - No
@importresolution beyond the built-in assets. The bundle throws "The browser build does not support @import for """ for unknown IDs. - No source maps. They aren't useful in this mode and the bundler skips them.
- No optimization step. No Lightning CSS in the bundle.
Performance instrumentation
packages/@tailwindcss-browser/src/instrumentation.ts reports timings to the browser performance timeline. Open DevTools → Performance, record a load, and you'll see Tailwind: Compile, Tailwind: Build, Tailwind: Reading Stylesheets, and per-build markers. This makes it easy to see how much rendering cost the framework is adding to a page.
Tests
packages/@tailwindcss-browser/tests/ runs Playwright UI tests (pnpm test:ui includes them). They drive a real browser via listhen (a small HTTP-server library) plus h3 (a route handler) and assert that DOM mutations result in the expected CSS being injected.
Integration points
- Depends only on
tailwindcss(workspace) at runtime. Devs useh3/listhenfor the test harness. - Bundles into a single global IIFE. The package's
mainandbrowserfields both point at./dist/index.global.js. - Has no peerDependencies. Anyone with a script tag can load it.
Entry points for modification
- Add support for a new asset import (e.g.
tailwindcss/forms): editloadStylesheetinindex.tsto recognize the ID, and add the file's content toassets.ts. - Change rebuild scheduling: edit the
MutationObservercallbacks inindex.ts. ThebuildQueuePromise is the serialization point. - Add new instrumentation timings: extend
instrumentation.ts; the class wrapsperformance.markandperformance.measure.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.