Curriculum1 of 8
Module 05

CSS Craft

Recreate a real design pixel for pixel, then add a transition only where it helps someone follow the change.

Phase 0 · Foundations · 8 sections · about 5 minutes

01 / 08

Before you start

Module promise: You're going to pick a well-designed screen from a product you admire, maybe a Stripe pricing card, an Apple product section, or a Linear workspace header. You'll recreate it by hand in HTML and CSS: structure, typography, spacing, color, box shadows. Then you'll add a single, purposeful transition that deepens the interaction without calling attention to itself.

By the end, you'll have a piece of work that demonstrates that craft and restraint are the same skill.


02 / 08

Web Typography Depth

Every typeface has an x-height, the height of a lowercase "x". This is not the same as the font size. An 18px serif typeface might have a much smaller x-height than an 18px sans-serif, which is why:

  • Changing font-family without adjusting line-height feels janky
  • A headline that looks perfect in one typeface looks broken in another at the same size

The relationships that matter:

JavaScript
1line-height = font-size × scale
2// A typical ratio for body text:
3line-height: 1.5 // 16px × 1.5 = 24px of vertical space
4// Headings usually go tighter:
5line-height: 1.2 // 32px × 1.2 = 38.4px of vertical space

Measure these ratios in the reference you pick. Don't eyeball them.

Professional designs rarely have random sizes. They use a scale. The most common:

  • Major third (1 : 1.25 × scale): 12 → 15 → 19 → 24 → 30 → 38 → 48px
  • Perfect fourth (1 : 1.33 × scale): 12 → 16 → 21 → 28 → 37 → 49px
  • Golden ratio (1 : 1.618 × scale): rare, feels sophisticated, hard to implement

Most professional products use major third or major fourth. Pick the reference, measure the sizes, find the scale.


03 / 08

Faithful Layout Recreation

Pick a screenshot from a real product you admire. It should be:

  • Self-contained: a card, a pricing section, a hero, a toolbar
  • Well-designed: you can articulate why the spacing and type feel good
  • Realistic complexity: roughly 10–50 elements
  • Public: you can screenshot it yourself

Some reference ideas: Stripe pricing card, Apple product specs section, Figma component panel header, Linear issue card, Vercel pricing toggle.

Before you code, measure it:

  1. 1Take a screenshot
  2. 2Measure font sizes using DevTools (Inspect → computed styles)
  3. 3Measure spacing: margins, padding, gaps
  4. 4Note colors and type weights
  5. 5Screenshot hover states if any

Write these measurements down.

Recreate it in HTML and CSS without copying code from the reference:

  • No screenshots pasted in. Build the real thing.
  • Measure, don't guess. If the reference has 16px of space, use 16px.
  • Match the type exactly: size, weight, line-height, letter-spacing.
  • Use the same fonts (or reasonable fallbacks).
  • Match colors precisely.

Aim for: lay the original and your version side-by-side and they look identical.


04 / 08

Motion That Means Something

Motion is feedback. It answers these questions:

  1. 1What changed? A button fills with color on hover → visual confirmation
  2. 2Where did it go? An element slides out → I understand where it moved
  3. 3Is it interactive? A slight scale on hover → "I can click this"
  4. 4What state am I in? An underline animates under the active nav item → "I'm here"

The best transitions are subtle. They should feel necessary, not ornamental.

Delete motion if:

  • It doesn't answer one of the four questions above
  • It takes longer than 300ms (slow animations feel laggy)
  • It repeats on every interaction (distracting)
  • It drops frames or causes layout shifts (jank ruins everything)
  • The user has prefers-reduced-motion (always respect this)

For this module, add one transition. It should:

  • Last 150–300ms
  • Use transition (not animation)
  • Respect prefers-reduced-motion
  • Make sense to the interaction

The transition should be good enough that if you remove it, the interaction feels dead. Not good enough that it steals the show.


05 / 08

Build: Recreate a Component

  1. 1Pick a reference: a component from a product you admire
  2. 2Take a screenshot in its default state
  3. 3Measure it: sizes, spacing, colors, typography
  4. 4Build it: HTML + CSS, pixel-faithful
  5. 5Add one transition: a hover state or focus effect that feels necessary
  6. 6Test it: compare side-by-side with original
Finished piece
Pricing card, with one transitionwhat you are aiming for

Check yours against this

  • The badge is 12px, uppercase, medium weight and muted
  • The amount is 48px, the currency 18px and the billing period 14px, all sitting on one baseline
  • Features are 14px and line up with their markers, not with the text above them
  • Hover deepens the shadow and lifts the card by 4px over 200ms, and nothing else moves
  • The transition is switched off under prefers-reduced-motion

Stripe pricing card example:

  • Badge: 12px, all caps, medium weight, muted color
  • Headline: 28px, bold, dark color, tight line-height
  • Price: 48px bold for amount, 18px for currency, 14px for billing period
  • Features: 14px, regular, aligned with bullets
  • Button: 14px, bold, white on color

On hover, the shadow deepens and the card shifts up slightly:

CSS
1.card {
2 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
3 transition:
4 box-shadow 200ms ease-out,
5 transform 200ms ease-out;
6}
7 
8.card:hover {
9 box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
10 transform: translateY(-4px);
11}
12 
13@media (prefers-reduced-motion: reduce) {
14 .card {
15 transition: none;
16 }
17}

06 / 08

Checkpoint

Before you ship:

Layout Accuracy

  • Measure your version. Spacing within 2–3px
  • Type sizes match exactly
  • Colors are hex-perfect
  • Borders and shadows match

Typography

  • Headline noticeably larger than body
  • Type weights create visual separation
  • Line-height feels generous (not cramped)
  • Letter-spacing correct on badges

Motion Quality

  • Lasts 150–300ms
  • Answers a question: "what changed?" or "can I interact?"
  • Doesn't drop frames
  • No layout shift
  • Respects prefers-reduced-motion

07 / 08

Reflection

Most people never notice spacing or a transition that is working. They notice the opposite: lines of text set too close together, or a panel that appears so abruptly they cannot tell what changed.

So when you are about to add a transition, ask three questions:

  • Does this change need to be seen at all?
  • Does showing it help someone understand what just happened?
  • Would the interaction be harder to follow without it?

Add it only when the answer to all three is yes.

Three considered transitions in a product are easier to use than thirty, because each one that survives is carrying information rather than competing for attention.


08 / 08

Next

Module 6 turns to JavaScript, the language that makes an interface respond to anything at all. You go from values and functions to a working search filter you built from an empty file.

Until then: measure everything, question every pixel, and delete the transitions that don't earn their place.

Up nextJavaScript fundamentalsNext modulePreviouslyCSS as a system