Shared Design System

Learning Website: Framework & Architecture

Chapter 4 · The Shared Design System

If the site becomes several sites, they still have to look like members of one family. A visitor who moves from the languages site to the programming site should not feel they have changed websites, and you should not have to change a colour in fifty places. This chapter looks at what the fragments really contain, chooses a design approach that does not require rewriting thousands of files, and sets up the pieces: tokens, shared components, and a safe order for loading them.

Measured, not assumed
The numbers in this chapter come from a script run over your real fragments (Exercise 1). They show a simpler situation than you might fear: a lot of repetition, but only a few real differences.

What the Fragments Contain Today

Every fragment carries its own <style> block, scoped under its own wrapper class. That scoping rule is deliberate, because it stops one fragment's styles leaking into another. The price is repetition. Measuring a few courses gives these results:

FolderFilesDistinct coloursCSS as share of file bytesAccent colour
Hungarian Basic 3121413%#5aa469
German Basic 1121417%#e0a82e
Japanese Basic 1121417%#d64550
Apache In Depth101930%#f0938a
Sidebar: programming301612%#6ee7b7
Django rebuild122132%#44b78b

Four findings stand out:

  1. The three language courses share the same palette. Surfaces #1a1d27, borders #2a2d3a, text #e2e4ec, muted #a0a4b8 and the purple speaker colour #7c5cbf are identical. Only the accent changes.
  2. There are two neutral families. The technical courses use a different set (#161b22 surfaces, #30363d borders, #c9d1d9 text, #8b949e muted), with their own accent per course.
  3. The same CSS is copied into every chapter. A colour used nine times in one chapter appears 108 times in a 12-chapter course.
  4. CSS is 12% to 32% of each file. A visitor downloads the same styling again on every chapter.

Three Ways to Share Design

ApproachHow it worksVerdict
A. Leave it as it isEach fragment keeps its own CSS. Sites only share the surrounding layout.Nothing to migrate, but the repetition stays and a brand change means editing every file.
B. One shared stylesheet, all at onceStrip the CSS from every fragment and rely on shared rules.Cleanest result, but a very large rewrite that touches every file and risks broken pages.
C. Tokens and shared components, graduallyDefine colours and spacing as variables. Provide shared component styles as defaults. Fragments keep working and are migrated when convenient.Recommended: safe at every step.

This course follows approach C. New content uses the shared layer, and old content keeps its own rules until it is rewritten or migrated by script.

Design Tokens

A token is a named value: --surface instead of #1a1d27. In CSS the natural way to hold tokens is custom properties (variables). They cascade, so you can set a different value for a part of the page, which is exactly what per-language accents need.

/* tokens.css: the only place a colour is decided */ [data-family="language"] { --surface: #1a1d27; --border: #2a2d3a; --text: #e2e4ec; --muted: #a0a4b8; } [data-family="technical"] { --surface: #161b22; --border: #30363d; --text: #c9d1d9; --muted: #8b949e; } .hu-lesson { --accent: #5aa469; } /* Hungarian */ .de-lesson { --accent: #e0a82e; } /* German */ /* a component using them */ .tip-box { background: var(--surface); border: 1px solid var(--border); border-left: 4px solid var(--accent); color: var(--text); }

Two neutral families can stay as they are for now. Unifying them into one is a design decision for later; tokens make it a small change when you choose to do it, because every use reads the variable.

A safe way to introduce the variable

Existing fragments hard-code their accent. A script can replace each occurrence inside the style block only with var(--accent, #5aa469). The second argument is a fallback: if the variable is not defined, the browser uses the old colour, so the page looks exactly as before. That makes each file's migration independently safe (Exercise 2).

Why one variable pays off
The Hungarian, German and Japanese chapters differ from each other by a single accent colour. Once that colour is a variable, a new language is one line of CSS instead of a whole copied stylesheet.

Shared Components

The fragments already use the same class names for the same ideas, which is what makes a shared layer possible:

GroupClasses in common use
Page header.lesson-head, .course-name, .chapter-sub
Callouts.tip-box, .warn-box, .finding-box
Code.code-block, .code-block-wrap, .copy-btn
Tables.compare-table, .vocab-table
Exercises.challenge-block, .ch-num, .ch-body, .ch-link, .quick-ref
Language lessons.dialog-grid, .dialog-card, .vocab-table, .tip-box

A shared components.css styles these once, using the tokens. Fragments that still carry their own rules for the same classes keep winning, as the next section explains.

Who Wins When Rules Collide

The existing scoping rule has a useful side effect. A fragment's rule such as .hu-lesson .tip-box is more specific (two classes) than a shared .tip-box (one class), so the fragment's own rule wins. If you write the shared rule with the same specificity, for example [data-site=languages] .tip-box, the two tie, and the later one in the document wins. The layout's stylesheet is in <head> and the fragment's <style> is inside the body, so the fragment still wins. Both effects protect old pages during a migration.

Do not reach for !important
When two rules clash it is tempting to add !important. It spreads, because the next rule that needs to override it needs one too. Fix a clash by changing specificity or by deleting the old rule.

Load order is therefore: tokens, then base, then components, then the fragment's own style. Exercise 3 writes the token file and checks the specificity rules with a small script.

Per-Area Accents

Colour already tells visitors where they are. These accents are in use today, with the contrast each one gives against the language surface #1a1d27:

AreaAccentContrast
Hungarian#5aa4695.56
German#e0a82e7.85
Japanese#d645503.86
French#4f8ef75.24
Links pages#38bdf87.85
Sidebar lessons#fbbf2410.07
Cheat sheets#6ee7b711.03

WCAG level AA asks for a contrast ratio of at least 4.5 for normal-size text. Every accent passes except the Japanese red, at 3.86. It is fine for borders, buttons and large headings, but weak for small coloured text. The right place to fix this is the token file, for instance with a separate lighter --accent-text for that language, not in each chapter (Exercise 3, part D).

Fonts, Printing and PDFs

  • Fonts are tokens too. The language lessons use a serif body font (Georgia) and the technical ones use the system sans-serif. Put each in a variable such as --font-body.
  • Web fonts and origins. If a font is loaded from another subdomain, the browser requires CORS headers (Learning Website: Framework & Architecture 1). The simplest options are system fonts, or self-hosting the same font files on every site.
  • The PDF builders render these same fragments. Any change to the shared layer changes the PDFs too, and the copy buttons are hidden for print by an @media print rule. After a design change, rebuild one chapter PDF and look at it before trusting the change.

Where the Design System Lives

The tokens, base and components are one small unit, shared by every site and versioned together. In a framework this becomes a package or a static folder:

design-system/ ├── tokens.css # colours, fonts, spacing ├── base.css # page, typography, links ├── components.css # tip-box, code-block, tables, exercises └── copy-code.js # the copyCodeBlock() function

Each site includes the same four files. The Django course (Learning Website with Django 5) serves them as static files, and the Next.js course (Learning Website with Next.js 5) imports them from a shared workspace package. A change in one place reaches every site on its next build, so treat the design system like code: review it and test it before release.

Hands-On Exercises

Exercise 1

Write palette.py, which reports for each folder of fragments the number of files, the number of distinct colours in the style blocks, the share of the file bytes that is CSS, and the most common colours. Run it on several courses from different areas and write down what is shared and what differs.

📄 View solution
Exercise 2

Write a script that replaces a fragment's accent colour, inside its <style> blocks only, with var(--accent, <the same colour>). Have it report the number of replacements and check that the braces are still balanced and the markup outside the style block is unchanged. Run it without modifying the original file.

📄 View solution
Exercise 3

Write a tokens.css for the two neutral families and the language accents. Then use a small script to compare the specificity of a shared .tip-box rule and a fragment's .hu-lesson .tip-box rule, state the load order, and check the contrast of every accent against the language surface.

📄 View solution

Chapter 4 Quick Reference

  • Every fragment carries its own scoped CSS: 12% to 32% of file size, copied into every chapter
  • The language courses share one palette and differ only in the accent colour; the technical courses use a second neutral family
  • Approach: tokens plus shared components, migrated gradually (not a one-off rewrite)
  • Tokens are CSS custom properties; set them per family or per wrapper so accents can differ by area
  • Migrate safely with var(--accent, #oldcolour): the fallback keeps old pages unchanged
  • A fragment's .wrapper .class rule beats a shared .class rule; on a tie the later one (the fragment's style) wins
  • Load order: tokens, base, components, then the fragment's own style
  • Avoid !important; fix clashes with specificity or by deleting the old rule
  • Check contrast: WCAG AA needs 4.5 for normal text, and the Japanese red #d64550 scores 3.86
  • PDF builders use the same HTML, so rebuild a PDF to check after a design change