Frontend & CSS¶
Zeython is a backend framework — it renders HTML via Jinja2 and Blade
(resources/views/, see Views and Blade) and
leaves the frontend build tooling up to you. It doesn't bundle a JS framework or a Vite/Node.js
pipeline. What it does ship is a real, production-ready Tailwind CSS
setup out of the box, because "unstyled HTML" is a bad first impression
for a new project, and a <script> tag pointed at a CDN is not something
you should actually ship.
What a fresh project has¶
resources/css/app.css # Tailwind source -- one line: @import "tailwindcss";
public/css/ # zeython css build writes app.css here (gitignored)
resources/views/welcome.blade.html's <head> picks between two things
depending on APP_DEBUG:
@if(debug)
<script src="https://cdn.tailwindcss.com"></script>
@else
<link rel="stylesheet" href="/css/app.css" />
@endif
APP_DEBUG=true(the default in a fresh.env): Tailwind's Play CDN. Zero setup, no build step, utility classes just work in any template — but it compiles every class in the browser on every page load, with nothing purged. Fine for local development, never used outside it.APP_DEBUG=false: the compiled stylesheet at/css/app.css, served by aStaticFilesmount already wired up inmain.py. Minified, and purged of every class your templates don't actually use.
Building it¶
Downloads the standalone Tailwind CSS CLI
— the real Tailwind compiler, with no Node.js or npm involved — caches it
in .tailwind/ (gitignored), and compiles resources/css/app.css to
public/css/app.css. Supports Linux (x64/arm64), macOS (Intel/Apple
Silicon), and Windows x64; see zeython css build --help for --binary
if you're on a platform without a standalone build (e.g. musl-libc Linux
like Alpine) and have your own tailwindcss executable to point at
instead.
By default this fetches whatever GitHub's latest release tag currently
resolves to, over plain HTTPS with no signature or checksum check before
running it — fine for a local dev machine, not something a CI pipeline or
production build should settle for. Pin it instead:
zeython css build --tailwind-version v3.4.13 --tailwind-sha256 <known-good-digest>
# or via environment variables, e.g. in your CI config:
# TAILWIND_CSS_VERSION=v3.4.13 TAILWIND_CSS_SHA256=<known-good-digest>
--tailwind-version pins the exact release fetched instead of trusting
latest not to change under you; --tailwind-sha256 verifies the
download against a checksum you computed yourself against a copy you
trust, refusing to run anything that doesn't match. Either can be set
without the other; both are opt-in so the zero-config default keeps
working.
Same thing, but rebuilds on every change to resources/css/ or any
template — leave it running alongside zeython serve while you work, the
same way you'd run npm run dev in a Node-based project (just without
the npm).
Tailwind v4 is CSS-first: there's no tailwind.config.js to maintain,
and utility classes are picked up automatically from every template in
the project — no content globs to keep in sync. Customize the design
system directly in resources/css/app.css with an @theme block:
Production images build it for you¶
The generated Dockerfile runs zeython css build in its builder stage
and copies only the compiled public/css/app.css into the final image —
not the ~100MB standalone Tailwind binary itself. A docker build (or any
CI pipeline that uses this Dockerfile) always ships the compiled,
production stylesheet automatically; there's no manual step to remember
before deploying.
If you don't want Tailwind at all¶
Delete resources/css/app.css, the @if(debug)...@endif block in
welcome.blade.html's <head>, and the zeython css build line from the
Dockerfile. Write plain CSS, or drop in any other framework — nothing
else in Zeython depends on it.
A Node-based pipeline instead¶
If your team already has a Vite/esbuild/webpack setup, there's nothing
Zeython-specific about wiring it in: build to a directory (public/css/
or wherever), and it's already mounted at /css in main.py via
Starlette's StaticFiles — the same way StorageServiceProvider serves
uploads. Point your build's output there, or mount a different path for a
different directory.