Templates and Design
Learning Website with Django
Chapter 5 · Templates & the Shared Design System
The database now holds every page; the routing knows which site a request is for. This chapter makes the pages
appear: a base template, a reusable theme app that carries the shared design, and a real
page view that replaces the placeholders from Chapter 2. The design follows the plan in Learning Website:
Framework & Architecture 4: tokens, shared components that sit under each lesson's own styles, and a
safe order for loading them. Here it becomes working code, and it was looked at in a real browser.
The Theme App
Everything visual lives in one app, apps/theme, so a change to the design is a change in one place
and every site gets it:
One Element Restyles the Page
The base template puts three facts about the site on the <html> element:
data-familypicks the neutral colours. The language courses use one set and the technical courses another (the two sets measured in the framework course), sotokens.cssdefines each set once under[data-family="..."].style="--accent: ..."sets the site's own colour, a CSS variable that links, the site bar's underline, table headings and the shared boxes all read.- The values come from one dictionary in the site map,
SITE_STYLE, so a ninth site is one more line and no template changes.
The four language colours (Hungarian green, German gold, Japanese red, French blue) are set on the lesson's
own wrapper, in tokens.css. A Hungarian chapter therefore keeps its green inside a site whose
chrome is violet, which is what the screenshot of the languages site shows.
Shared Defaults Under Old Styles
Every existing page carries its own scoped CSS. If the shared styles beat it, old pages change; if they lose, old pages stay exactly as they are and new pages use the shared look. The cascade gives you the second for free, provided the shared rules stay less specific than a fragment's own:
| Rule | Specificity | Where it comes from |
|---|---|---|
.tip-box | one class | components.css (shared default) |
.devsetup1-lesson .tip-box | two classes | the fragment's own style: wins |
| A tie | equal | the later rule wins, and the fragment's <style> is in the body, after the links |
Two tests keep that promise: the shared CSS never contains !important, and no shared selector uses an
id or more than two parts. The screenshot of the systems site shows the result: the Debian chapter keeps its pink
headings, green tip box and yellow warning box, inside a site with a salmon accent.
Accents You Can Read
A colour that is lovely and unreadable is a bug. Each accent has a test that checks its contrast against the surface it sits on and against the page background, to the 4.5 that WCAG level AA asks for normal text:
| Site | Accent | On surface | On background |
|---|---|---|---|
| languages | #a78bfa | 6.18 | 6.93 |
| webdevelopment | #38bdf8 | 8.07 | 8.81 |
| programming | #44b78b | 6.91 | 7.54 |
| systems | #f0938a | 7.62 | 8.31 |
| ai | #fbbf24 | 10.36 | 11.30 |
| humanities | #fb923c | 7.43 | 8.34 |
| lifeskills | #6ee7b7 | 11.03 | 12.38 |
| creative | #f472b6 | 6.53 | 7.12 |
Every site's accent passes, and no two are the same. These are the site accents; the Japanese lesson red
is the lessons' own colour and is still only 3.86 (Learning Website: Framework & Architecture 4), which a
lighter text variant in tokens.css would fix.
The Page View
- The address is the path.
/hungary/x/chapter/is the filehungary/x/chapter.html. There is no other lookup key. - The site check is in the query. A row whose stored site disagrees with the address is a 404, even though the route matched.
- The heading rule is the live site's. A page without its own
<h1>gets one made from its title (course chapters have no real heading of their own); a page with one does not get a second. - Escaping is on by default. A page's title is escaped; its stored body is marked safe in exactly one place (Chapter 4). A test stores a title of
<b>x</b>and a body of<b>y</b>and checks that the first is shown as text and the second as markup.
django.urls.resolve, and “which site answered?” looks for data-site in the page.
A routed-but-missing page and an unrouted path are both 404 now, so a status code can no longer tell the two apart.
Looking at It
The tests cannot say whether a page looks right. Serving the imported content and photographing it with headless Chrome (mapping the site names to your own machine inside the browser, so no hosts file is needed) showed:
- Languages: a violet-underlined site bar; the chapter title as a large heading, made from the banner; the course name in the lesson's own green; dialog cards with purple speaker letters.
- Systems: the same layout with a salmon underline; the Debian chapter in its own pink, with its green tip box, yellow warning and table.
Only desktop width was looked at, and a third screenshot (programming) was taken but not examined. A phone width and the print preview remain to check (Learning Website: Framework & Architecture 12).
Hands-On Exercises
Build the theme app: a SITE_STYLE dictionary, a context processor, the three stylesheets (tokens, base, components), the copy-code script, and the base, page and home templates. Explain how one element restyles a page and why a fragment's own CSS still wins.
Replace the placeholder views with a real page view, rewrite the tests that depended on the placeholders, and add tests for contrast, the shared CSS rules, stylesheet order, escaping, the heading rule and the 404 cases.
📄 View solutionServe the imported content with the development server, fetch a stylesheet with curl, and photograph one page from two different sites with headless Chrome. Look at the images and write down what you see, and what you did not check.
Chapter 5 Quick Reference
- One
themeapp holds the whole design:tokens.css,base.css,components.css,copy-code.jsand the templates - The
<html>element carriesdata-site,data-familyandstyle="--accent: ..."; the values come fromSITE_STYLE - Load order: tokens, base, components, then the page body (with the fragment's own
<style>) - Shared rules stay at one class, with no ids and no
!important, so a fragment's own two-part rule wins - Language colours (
.hu-lessonand the others) live on the lesson wrapper, so they survive a different site accent - Every accent is tested at 4.5 or more on its surface and on the page background
- The page view: address to path, site checked in the query, one
<h1>only if the body has none, title escaped, body marked safe once - A test that asserts on placeholder output breaks when the placeholder goes: test routing with
resolve(), and the site withdata-site - Look at the pages in a real browser; headless Chrome with
--host-resolver-rulesneeds no hosts file