Skip to content

Design system

Every colour, size and component on this page comes from the shared tokens. Switch the theme or language in the header to check both.

Colour

  • Paper--paper
  • Surface--surface
  • Sunken--sunken
  • Ink--ink
  • Ink, muted--ink-muted
  • Accent, Software Engineering--accent
  • AI Engineering--track-ai
  • Marker--marker
  • Marker ink, focus--marker-ink
  • Control border--border-strong

Typography

Annotated engineering, one idea at a time

Closures keep their surroundings

A closure is a function bundled with the variables that were in scope when it was created. It can read and update them long after the outer function has returned.

Interface text uses the grotesk at 16 pixels.

const greet = (name: string) => `Hello, ${name}`;

Controls

View curriculum
Software EngineeringAI Engineering

Reading surface

The reading surface below renders a sample lesson through the content engine.

Sample course7 min

Reading a lesson in the margin

A sample lesson that uses every reading component, so the design system can be checked in both themes and on every screen size.

Why the margin matters#

A good technical book explains the idea in the main column and keeps the asides close by: the why, the caveat, the trade-off. Courses does the same. On a wide screen the notes sit in the margin beside the paragraph they belong to; on a phone they appear right after it.

Margin notes hold short context. Anything longer belongs in the main text.

Inline code such as Array.prototype.reduce and links to the MDN reference use the same reading rhythm as the prose.

Code with callouts#

Numbered callouts tie a line of code to a note in the margin, so the explanation stays next to the line it explains.

debounce.ts
export function debounce<T extends unknown[]>(fn: (...args: T) => void, wait = 200) {  let timer: ReturnType<typeof setTimeout> | undefined;  return (...args: T) => {    clearTimeout(timer);1    timer = setTimeout(() => {      fn(...args);    }, wait);2  };}

Every new call cancels the pending one, so only the last call in a burst runs.

wait defaults to 200 ms, which is about how long people pause between keystrokes.

Callouts#

Note

A note adds context without interrupting the explanation.

Run it yourself

Paste the function into your browser console and call it several times in a row.

Warning

A debounced function returns nothing useful: its result arrives later, if at all.

Debounce or throttle?

Debounce waits for quiet; throttle runs at most once per interval. Search boxes usually want debounce, scroll handlers usually want throttle.

Comparing approaches#

Debounce

  • Runs once, after the calls stop
  • Good for search-as-you-type

Throttle

  • Runs at a steady rate while calls continue
  • Good for scroll and resize handlers

Diagrams#

A request travels from the browser to a route handler, then to a model API, and the response streams back to the browser.A request travels from the browser to a route handler, then to a model API, and the response streams back to the browser.
Diagrams are drawn SVG with a variant for each theme.

Video#

Plays from YouTube in privacy-enhanced mode. Nothing loads until you press play.Watch on YouTube

Transcript

The transcript sits under the player, so the lesson still works when the video can't be watched.

Practice#

ExerciseCancel a debounced call

Add a cancel() method to the debounced function that clears the pending timer.

Show solution

Return the inner function with a cancel property that calls clearTimeout(timer).

A coding challenge for this lesson is coming later.

ApproachRunsTypical use
DebounceAfter the calls stopSearch input
ThrottleAt most once per intervalScroll handler

The best explanation is the one the reader can repeat in their own words.

  1. Read the explanation.
  2. Run the example.
  3. Change one thing and predict the result.