The Languages Site
Learning Website with Next.js
Chapter 7 ยท The Languages Site
The earlier chapters built the machinery; this one finishes the first real site. The languages site needs a front page that shows each language and its courses in the order a learner meets them, a colour for each language that stays readable, and answer blocks that work. As before, the grouping is checked against the Django project on the real content, and the answer blocks are tested in a real browser.
The Front Page: Grouped the Way a Learner Thinks
It is not an alphabetical list of folders. Each language has its courses in the order they are met:
| Group | Holds | How a course is placed |
|---|---|---|
| Reading and writing | Alphabets and kana | The folder name contains alphabet, hiragana, katakana or kanji; ordered by that, then course number |
| Survival <Language> | Basic Conversation 1 to 3 | The folder name contains -basic- and the course number is 3 or less |
| Everyday <Language> | Basic Conversation 4 to 6 | The same, with a number above 3 |
| Culture | Courses about the country | The folder is under culture/; ordered by name |
| Other courses | Anything else with numbered chapters | Ordered by name |
This is decided by plain functions in a package, @lw/languages-site, so it needs no browser to test. A new course
appears on the front page when its folder first has a numbered chapter: there is no list to edit. The real site has 8 groups
over 20 courses: French has Survival French; German has Survival and Everyday German; Hungarian has Reading and writing and
Survival Hungarian; Japanese has Reading and writing (5 courses), Survival Japanese and Culture (5 courses).
Is It the Same as Django's?
A script in the Django project writes what its front page holds; the TypeScript version is asked the same question:
| Language | Groups | Courses | Lessons | Compared with Django |
|---|---|---|---|---|
| French | 1 | 1 | 3 | identical |
| German | 2 | 4 | 0 | identical |
| Hungarian | 2 | 4 | 12 | identical |
| Japanese | 3 | 11 | 300 | identical |
- “Basic 3” treated as Everyday (
<= 2instead of<= 3): German and Hungarian came out different. Caught. - Sort changed to
localeCompare(the browser's language rules instead of code-point order): 0 different on the real content. Not caught, because the five Japanese culture courses happen to sort the same either way. A unit test with chosen awkward names (Banana,Zebra,apple) does catch it (55 passed, 1 failed).
A Colour for Each Language, and Text You Can Read
Each language's section carries two CSS variables, --lang-accent (the border) and --lang-text (the
heading text), set inline on the section. French is blue, German gold, Hungarian green and Japanese red, from their flags. One
catch: Japanese's red (#d64550) is only 3.86 against the dark surface, and text needs 4.5. So Japanese keeps its true
red for the border and uses a lighter red of the same hue (#e8707a) for its heading. A test keeps every
language's text colour at 4.5 or more, reads the real surface colour out of tokens.css, and checks that Japanese's
accent alone would fail, which is why the second colour exists.
The page's stylesheet is a CSS Module: only this page uses those classes, so by Chapter 5's rule they are not global. A screenshot in headless Chrome shows a coloured left border and name for each language, the group titles in small capitals, and each course with its chapter count.
Course and Chapter Pages
Nothing language-specific was needed. A course is a folder listing headed with the course's name, and a chapter has a breadcrumb
and previous and next links, all from Chapter 6. The lessons themselves keep their own accent colour (green Hungarian, gold
German, red Japanese, blue French) from their own scoped style, as Chapter 5 found, and their lang marks from
Chapter 4.
Answer Blocks
Most language lessons end with a “Try It Yourself” block whose answers are hidden until asked for. In the files they are
plain <details> elements with a <summary> (“Show answer”): no script, so nothing to
re-run and nothing to break. 196 of the site's 601 pages contain them. That is a claim about a real browser, so it was tested in
one:
| German Basic Conversation 1, chapter 1 (5 blocks) | First block |
|---|---|
| Before the click | closed, 24 px high |
| After one click on “Show answer” | open, 60 px high |
| After a second click | closed, 24 px high |
<details> still reports a size for its
content. The takeaway: a check that gives the same answer in both states checks nothing, so before trusting one, run
it in a state where the answer is known to be “no”. It was replaced with the height of the whole block, which does change.
<details> with Enter or Space on its summary, but that was not
tried), a screen reader, other browsers, and the print layout. Only the languages site is complete; the others have a menu but no
pages yet.
Hands-On Exercises
Group the language courses the way a learner meets them (reading and writing, survival, everyday, culture, other), with readable text colours for every language. Prove the result against the Django project on the real content, and show with planted mistakes what the real data does and does not catch.
๐ View solutionBuild the languages front page as a server component with a CSS Module, a section and colour for each language, and a lessons line only where lessons exist. Build the site, fetch the page and check its sections, groups and colours.
๐ View solutionTest in a real browser that a lesson's “Show answer” blocks open and close. Make sure your measurement gives different answers in the open and closed states, and say what you did not test.
๐ View solutionChapter 7 Quick Reference
- Front page: each language (French, German, Hungarian, Japanese), its courses in groups: Reading and writing, Survival, Everyday, Culture, Other courses
- Group by folder name and course number; sort numbers by number and names by code point (not
localeCompare) - Identical to Django: 4 sections, 8 groups, 20 courses; a "Basic 3 is Everyday" mistake was caught, a sort mistake was not (a unit test catches it)
- Two CSS variables per language section:
--lang-accent(border) and--lang-text(heading); Japanese's text red is lighter for contrast 4.5 - The front page's classes are a CSS Module; the data comes from
groupCoursesat build time, so a new course appears by itself - Course and chapter pages are Chapter 6's: a course is a listing headed with its name, a chapter has a breadcrumb and previous/next
- "Try It Yourself": plain
<details>, no script; tested: closed 24 px, open 60 px, closed again - A check that cannot tell open from closed checks nothing: run it in a state where the answer is "no"
- Not tried: keyboard, a screen reader, other browsers, print