Styling — Dark Theme
Website Rebuild with Express
Chapter 5 · Styling — Dark Theme
Every sibling course in this series had some asset story, however minimal. Express's own is the most minimal of all six.
Serving a Plain Stylesheet
Genuinely More Minimal Than Even Rails' Propshaft
express.static() does none of that. It's a literal raw static-file server, serving bytes from disk unchanged, with no fingerprinting, no cache-busting, no compilation step of any kind — Express's own standard middleware for a job every basic web server already knows how to do, offered here with zero enhancement on top.
A Real, Honest Operational Gap
express.static() serves the file at the exact same URL every time, a visitor's browser (or an intermediate proxy) may keep serving a stale, cached copy of global.css after an update, with nothing in this setup to signal that the file changed. A manual versioned query string — /css/global.css?v=2, bumped by hand on every real change — is the honest, low-tech fix; there's no automatic equivalent to Propshaft's own fingerprinting here.
Scoped Styles Don't Apply Here at All
Astro's own scoped <style> blocks worked because Astro has real components with real boundaries to scope styles to. EJS partials have no such boundary — include() just inlines one template's own output into another's, so the entire concept of "scoping" a style to one partial doesn't have anywhere to attach. This isn't a missing feature so much as a question that doesn't arise in a template-inclusion system the way it does in a component system.
The Full Asset-Bundling Spectrum, Closed Out
| Next.js | Django | Laravel | Rails | Astro | Express | |
|---|---|---|---|---|---|---|
| Approach | Bundles by design | Ships nothing | Pre-wires Vite | Propshaft + Importmap | Scoped styles + plain import | express.static() |
| Cache-busting? | Yes, automatic | No | Yes, via Vite | Yes — content-hash fingerprinting | Build-time hashing | None — manual only |
Hands-On Exercises
Set up express.static('public'), link global.css from the header partial, and confirm the dark theme applies site-wide.
📄 View solutionUpdate global.css, reload without a query-string change, and confirm a browser may still serve the stale cached version — then fix it with a manual ?v= bump.
📄 View solutionCompare the bytes of a served CSS file directly against the source file on disk, and confirm they're identical — no fingerprinting, hashing, or transformation applied at all.
📄 View solutionChapter 5 Quick Reference
express.static('public')— a raw static-file server, zero processing- More minimal than Rails' Propshaft — no fingerprinting, no cache-busting at all
- Real gap — a manual versioned query string is the only cache-busting available
- Scoped styles don't apply — no component boundary exists to scope them to
- Next chapter: The Database: Raw SQL, No ORM