app/globals.css. The output ships as a content-hashed <link rel="stylesheet">, injected directly into <head>. There is no flash of unstyled content (FOUC), no Tailwind Play CDN script, and no separate build step.
Setup
app/globals.css:
pylon dev picks up the file on the next bundle and emits /_pylon/build/styles-<hash>.css. The SSR runtime adds <link rel="stylesheet"> to every page’s <head>.
You do not need a tailwind.config.js. Tailwind v4 uses CSS-first configuration. Theme tokens go in the @theme block:
bg-brand, text-brand, and font-sans are now available throughout your app.
How the head injection works
Tailwind in SSR has a timing problem: your root layout renders the<head> before the SSR runtime knows which CSS file to include. Pylon solves it by rewriting React’s HTML output as it streams.
- The bundler compiles
app/globals.cssintostyles-<hash>.cssin the build output. - On render, the SSR runtime reads the manifest to find the hash.
- As React’s HTML stream flows past, the runtime watches for
</head>(with a small carry buffer to handle chunk-boundary splits) and splices in<link rel="stylesheet" href="/_pylon/build/styles-<hash>.css">before the close tag. - The browser starts fetching the CSS while it is still parsing the body. There is no FOUC and no extra round trip.
RootLayout does not need to know anything about it. Write the head you want. Pylon adds the right links.
Class scanning
Tailwind v4 scans your source files for class names at build time. The@source directive in globals.css controls which files it scans:
@source, Tailwind’s auto-discovery walks package.json dependencies and likely-looking source folders. Adding the directive is more explicit, and it survives unusual layouts.
Hot rebuild
pylon dev rebuilds the bundle (and the Tailwind CSS) when you save a source file. Refresh the browser to see styles update. Hot reload without a page refresh for CSS is on the roadmap.
Production
The flow is the same.pylon (the production binary) compiles Tailwind at boot. The output is fingerprinted, so CDNs and browsers cache it indefinitely. Editing globals.css produces a new hash, which breaks the cache automatically.
There is no separate build step. The single binary handles development and production identically.
Other CSS
Tailwind is the recommended path because it is wired into the bundler. For other CSS, such as vanilla CSS, a different framework, or CSS-in-JS, you have two options: Stylesheet inweb/dist/: drop a styles.css next to your other static assets and link to it from your root layout:
packages/functions/src/ssr-client-bundler.ts, and we are glad to extend it.