Styling — Dark Theme

Website Rebuild with Django

Chapter 5 · Styling — Dark Theme

Website Rebuild with Next.js 5 gave the rebuilt site its dark theme through Next.js's own bundling pipeline. Django has no equivalent built-in bundler at all — its staticfiles app organizes and serves plain files directly, nothing more. That's a genuine difference worth understanding before writing a single line of CSS, not just a syntax swap.

Django's staticfiles App

<!-- content/templates/content/base.html --> {% load static %} <!DOCTYPE html> <html lang="en"> <head> <link rel="stylesheet" href="{% static 'content/style.css' %}"> </head>

{% load static %} makes the {% static %} tag available in that template; {% static 'content/style.css' %} resolves to the correct served URL for that file, wherever STATIC_URL actually points.

Why static/content/..., Not Just static/...

content/ ├── static/ │ └── content/ │ └── style.css # the extra "content/" nesting is deliberate ├── templates/ │ └── content/ │ ├── base.html │ └── page_detail.html
The same namespacing idea as Chapter 4's templates, applied to static files
collectstatic (covered properly in Chapter 11) merges every installed app's own static/ folder into one single, flat STATIC_ROOT directory for production. If two different apps both had a static/style.css, one would silently overwrite the other during that merge — there'd be no error, just a mysteriously wrong stylesheet in production. Nesting each app's files under its own name (static/content/style.css) avoids the collision entirely, the exact same reasoning Chapter 4's templates/content/... nesting already relied on.

The Stylesheet: Reusing This Site's Own Established Palette

/* content/static/content/style.css */ :root { --bg: #0f1117; --surface: #161b22; --border: #30363d; --text: #c9d1d9; --accent: #44B78B; } body.dark-theme { background: var(--bg); color: var(--text); font-family: system-ui, sans-serif; }

These aren't arbitrary color choices — they're the exact same background, surface, border, and text colors every generated lesson fragment across this whole site already uses. The point of a rebuild is continuity: a visitor moving between an old page and a newly-migrated one shouldn't be able to tell which framework rendered it.

Development vs. Production: A Real Gotcha

Static files stop being served the moment DEBUG=False
Django's own dev server (runserver) serves static files automatically — but only while DEBUG=True. Chapter 11's own production setup requires DEBUG=False, and the instant that flag flips, Django stops serving static/ files itself entirely. A site that rendered its own CSS perfectly throughout every chapter of local development can suddenly appear completely unstyled the moment it's deployed — not a bug in the CSS, a missing production step: python manage.py collectstatic has to gather every app's files into STATIC_ROOT first, and something else (WhiteNoise, or nginx directly) has to actually serve from there, since Django itself won't.

No Built-In Bundler: A Genuine Difference From Next.js

Next.jsDjango
CSS handlingRuns through Next.js's own build pipeline — bundling, minification, and optimization all built inPlain files, served as-is by staticfiles — no bundling unless you add a separate tool
If bundling/SCSS is genuinely wantedAlready there by defaultA separate package (e.g. django-compressor) — an explicit, deliberate addition, not the default

For a project this size, plain hand-written CSS with no build step is a completely reasonable choice — but it's worth knowing the absence of a bundler is a real, structural difference from Next.js, not something this course is simply choosing to skip covering.

A forward reference
Chapter 11's own deployment work covers collectstatic and WhiteNoise in real depth — this chapter only needs to establish that the step exists and why, so it isn't a surprise later.

Hands-On Exercises

Exercise 1

Explain why this project's stylesheet lives at content/static/content/style.css — with "content" appearing twice — rather than simply content/static/style.css.

📄 View solution
Exercise 2

Explain why a Django site that displays its own CSS perfectly throughout local development can suddenly appear completely unstyled the moment it's deployed with DEBUG=False, and name the command that fixes it.

📄 View solution
Exercise 3

Explain the genuine structural difference between how Next.js handles CSS and how Django's staticfiles app handles it — not just "different syntax," but a real difference in what each framework does by default.

📄 View solution

Chapter 5 Quick Reference

  • {% load static %} / {% static 'path' %} — makes and resolves a static file URL inside a template
  • static/<app_name>/... — namespacing convention avoiding collisions once collectstatic merges every app's files into one folder
  • This site's own established dark-theme palette — reused deliberately, for real visual continuity across the rebuild
  • Static files are only auto-served while DEBUG=True — production needs collectstatic plus WhiteNoise or nginx
  • No built-in bundler — a genuine structural difference from Next.js, not just different syntax
  • Next chapter: The Database with Django's ORM