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
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.
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:
line-height = font-size × scale// A typical ratio for body text:line-height: 1.5 // 16px × 1.5 = 24px of vertical space// Headings usually go tighter:line-height: 1.2 // 32px × 1.2 = 38.4px of vertical spaceMeasure 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.
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:
- 1Take a screenshot
- 2Measure font sizes using DevTools (Inspect → computed styles)
- 3Measure spacing: margins, padding, gaps
- 4Note colors and type weights
- 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.
Motion That Means Something
Motion is feedback. It answers these questions:
- 1What changed? A button fills with color on hover → visual confirmation
- 2Where did it go? An element slides out → I understand where it moved
- 3Is it interactive? A slight scale on hover → "I can click this"
- 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(notanimation) - 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.
Build: Recreate a Component
- 1Pick a reference: a component from a product you admire
- 2Take a screenshot in its default state
- 3Measure it: sizes, spacing, colors, typography
- 4Build it: HTML + CSS, pixel-faithful
- 5Add one transition: a hover state or focus effect that feels necessary
- 6Test it: compare side-by-side with original
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:
.card { box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); transition: box-shadow 200ms ease-out, transform 200ms ease-out;} .card:hover { box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); transform: translateY(-4px);} @media (prefers-reduced-motion: reduce) { .card { transition: none; }}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
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.
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.