Styling
Popcorn Web does not force one CSS workflow. Component-scoped styles require no setup, while Tailwind CSS remains an opt-in build tool. The two approaches can also share a component without competing for its class names.
Component styles
Section titled “Component styles”A component contributes its own head content, and its class names are scoped automatically:
export component Card(label: string): html {<head><style>.box { color: red }</style></head><div class="box"><span>{label}</span></div>}Classes declared in the block are renamed and the matching class attributes
rewritten. Classes not declared there pass through untouched, which is
exactly what lets Tailwind utilities sit next to scoped rules in the same
attribute. See Templates.
Tailwind CSS
Section titled “Tailwind CSS”Popcorn Web drives the standalone Tailwind binary. There is no
package.json, no node_modules, and no Node lockfile.
The shortest path is to enable it when creating the project:
pw init myapp --tailwindEnabling it later
Section titled “Enabling it later”Adding Tailwind later requires the same four pieces that the scaffold would have created. Because the existing project may already contain custom files, add them explicitly.
1. Make the tailwindcss binary available.
pw dev and pw build look for tailwindcss on PATH and fail with a clear
message if it is missing. Add it to devbox.json:
{ "$schema": "https://raw.githubusercontent.com/jetify-com/devbox/0.14.2/.schema/devbox.schema.json", "packages": ["go@latest", "git@latest", "valkey@latest", "tailwindcss_4@4.1.18"], "shell": {"init_hook": ["echo 'Popcorn Web development environment'"]}}Then re-enter the shell so the new package is on PATH:
devbox shellThe version pin keeps the CSS build reproducible; without it, the same source can
produce different output as the tool moves. If another tool manager provides
tailwindcss on PATH, Devbox is not required.
2. Create the stylesheet entry point.
@import "tailwindcss";@source "../handlers";@source "../templates";The import starts Tailwind, but it does not tell Tailwind where templates live.
The @source lines expose class names inside .pw.html files; without them, the
generated stylesheet is nearly empty. Add one source per template directory.
For the layout in Project structure, that means
@source "../webroot";.
The @import "tailwindcss" line is checked before the build runs, so a
malformed entry point is reported rather than silently producing empty CSS.
3. Turn it on in popcornweb.toml.
[assets.tailwind]enabled = trueinput = "assets/app.css"output = "public/generated/app.css"minify = trueinput and output are relative to the project root and must differ. When
enabled is true and the paths are omitted, these two values are the defaults.
4. Link the output from the document shell.
package templates
external AssetURL(name: string): url
export component Document(children: html?): html {<!doctype html><html lang="en"><head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>My App</title> <link rel="stylesheet" href={AssetURL("generated/app.css")}></head><body><slot /></body></html>}The argument is the path of output inside public/; AssetURL supplies
server.public.mount (default /public) and the revision segment that lets a
deployment cache the stylesheet rather than revalidating it on every page load.
pw init --tailwind writes this shell and the Go behind the declaration. See
Static Assets for how that directory is
embedded and served, and for what the revision buys.
Then start the dev server:
pw devHow it runs
Section titled “How it runs”| Command | Behaviour |
|---|---|
pw dev |
one unminified build, then a watcher; the input is re-watched if the watcher exits |
pw build |
one minified build, regardless of minify in the file |
Output is first written to a temporary file and then renamed into place, so the
server never observes a half-written stylesheet. public/generated/app.css is
build output. The scaffolded .gitignore already excludes compressed
public/**/*.br, public/**/*.zstd and public/**/*.gz sidecars, and
generated CSS usually belongs in it too.
Plugins
Section titled “Plugins”Local plugins referenced from the entry point are resolved relative to it and verified to exist before the build:
@import "tailwindcss";@plugin "./plugins/typography.mjs";@source "../handlers";Keep the modules in the project — assets/plugins/*.mjs is the scaffolded
convention — so the build stays reproducible without a package manager.
