skip to content

How do you compute the vw plus rem preferred value inside clamp() so a font-size lands exactly on 16px at a 320px viewport and 24px at a 1280px viewport?

level: seniorimportance: should knowfreq 38%

answer

  1. two points define a line
  2. delta over delta
  3. the slope becomes a vw coefficient
  4. the intercept is the value at zero width
  5. express the intercept in rem, not px

basics

~20 s

Treat it as a straight line through two points: slope is (24 - 16) / (1280 - 320) = 0.00833, or 0.833vw, and the intercept is 16 - 0.00833 x 320 = 13.33px = 0.833rem. That gives font-size: clamp(1rem, 0.833rem + 0.833vw, 1.5rem).

solid answer

~50 s

You are fitting a straight line through two points, size against viewport width. The slope is the size delta over the width delta — (24 − 16) / (1280 − 320) = 0.008333 px of type per px of viewport — and since `1vw` is one percent of the width, multiplying by 100 gives the coefficient `0.8333vw`. The constant term is the line's value at zero width: 16 − 0.008333 × 320 = 13.333px, which at a 16px root is `0.8333rem`. So the declaration is `font-size: clamp(1rem, 0.8333rem + 0.8333vw, 1.5rem)`. Check it: at 320px you get 13.33 + 2.67 = 16px, at 1280px you get 13.33 + 10.67 = 24px. Two details matter — write the constant in `rem`, not px, so the value still tracks the reader's font-size preference, and remember the endpoints are exact only at the default 16px root size.

go deeper

for a junior

Recognise that a fluid size is a straight line between two viewport widths, and that the clamp() bounds are simply the two target sizes so the line stops at each end.

for a middle

Carry out the derivation: slope is the size delta over the viewport delta, times 100 for vw; the constant is the smaller size minus slope times the smaller viewport, converted to rem. Verify at both endpoints.

for a senior

Justify the unit choices and the tradeoff — a rem intercept keeps the curve responsive to the reader's font-size preference at the cost of exact endpoints at non-default root sizes — and pick anchor widths from where the content breaks, not from device names.

for a principal

Own the scale rather than the formula: a small set of derived steps published as tokens, each annotated with its two endpoint targets, prevents per-component slopes that cross over and invert hierarchy at unpredictable widths.

## The problem, stated as maths A fluid type step is a linear function of viewport width: `size = m × viewportWidth + b`. You are given two points — (320px viewport, 16px type) and (1280px viewport, 24px type) — and you need `m` and `b`, then you express them in CSS units. ## Step 1 — the slope ``` m = (maxSize − minSize) / (maxViewport − minViewport) m = (24 − 16) / (1280 − 320) = 8 / 960 = 0.008333… ``` That is px of type per px of viewport. CSS has no unit for "per pixel of viewport", but `1vw` equals one percent of the viewport, so the coefficient in `vw` is `m × 100 = 0.8333vw`. ## Step 2 — the intercept The constant is the line's value at a viewport width of zero: ``` b = minSize − m × minViewport b = 16 − 0.008333 × 320 = 16 − 2.6667 = 13.3333px ``` Divide by the 16px root font-size to express it relatively: `13.3333 / 16 = 0.8333rem`. (The two coefficients matching at 0.8333 here is a coincidence of these particular numbers, not a rule.) ## Step 3 — assemble and bound ```css h3 { /* 16px @ 320px viewport → 24px @ 1280px viewport */ font-size: clamp(1rem, 0.8333rem + 0.8333vw, 1.5rem); } ``` The bounds are the two target sizes in `rem`: `1rem` = 16px and `1.5rem` = 24px. Their job is to stop the line continuing past the design range — below 320px it would keep falling, above 1280px it would keep climbing. ## Step 4 — verify at both endpoints Always do this out loud in an interview; it is the step that proves you understand rather than recite a formula. - Viewport 320px: `0.8333vw` = 0.8333 × 3.2 = 2.667px; plus 13.333px = **16px**. ✓ - Viewport 1280px: `0.8333vw` = 0.8333 × 12.8 = 10.667px; plus 13.333px = **24px**. ✓ - Viewport 800px: 13.333 + 6.667 = 20px, exactly halfway — as a linear function should be. ## Why the constant must be in rem Writing `clamp(1rem, 13.333px + 0.8333vw, 1.5rem)` produces identical pixels at the default root size and is the more common mistake than it looks. But the `px` constant is frozen: a reader who raises their default font size moves only the bounds, while the whole mid-range stays put. Expressing the intercept in `rem` keeps the majority of the value tied to the root size, so the entire curve shifts with the preference. The tradeoff is that the endpoints are then exact *only* at the assumed root size — at a 24px root, the `rem` terms scale by 1.5 while the `vw` term does not, so the line no longer passes precisely through 16px/24px. That is the correct tradeoff: honouring the reader's preference beats hitting a designer's pixel target for a reader who never asked for it. ## Generalising it Once you have derived one step you have derived them all, and the arithmetic belongs in a variable rather than scattered through components: ```css :root { --step-0: clamp(1rem, 0.8333rem + 0.8333vw, 1.5rem); --step-1: clamp(1.25rem, 0.9167rem + 1.6667vw, 2rem); } ``` Deriving `--step-1` (20px at 320px → 32px at 1280px): slope = 12/960 = 0.0125 → `1.25vw`… note that publishing the two endpoint targets alongside each declaration, as a comment, is what makes the value auditable later — the numbers are unreadable on their own. ## Failure modes to mention **Sizing the vw coefficient from one endpoint.** Computing `24 / 1280 × 100 = 1.875vw` fits the wide end and misses the narrow one badly. The slope must come from the *delta* over the *delta*. **Forgetting the bounds.** Without `clamp()`, the same line gives 8px type at a 200px viewport and keeps growing forever on ultrawide monitors. **Choosing endpoints from device sizes.** The two anchor widths should come from where the content stops working, not from a phone model. **Every element on its own line.** If each component derives its own slope, sizes cross over at some viewport width and the hierarchy inverts. Deriving a small set of steps from one scale prevents that. **Fluid-sizing everything.** Borders, hairlines and small UI affordances rarely benefit; the arithmetic is worth it for type, spacing and measure.

  • Why express the constant term in rem rather than px when both give identical pixels at a 16px root?
    Because the px constant is frozen against the reader's font-size preference. With `13.333px + 0.8333vw`, only the clamp bounds move when someone raises their default size — the entire mid-range ignores them. In `rem`, the majority of the value scales with the root, so the whole curve shifts. The cost is that the endpoints are exact only at the assumed 16px root, which is the right trade.
  • What goes wrong if you compute the vw coefficient as 24px divided by 1280px?
    That gives 1.875vw, which fits the wide endpoint alone and treats the line as passing through the origin. At a 320px viewport it yields 6px of type instead of 16px, so the narrow end collapses. The slope must be the size delta divided by the viewport delta — here 8/960 — because the line has a non-zero intercept.
  • How do you keep several fluid steps from crossing over each other?
    Derive them all from one scale using the same two anchor viewport widths, so every step is a line over the same interval and their order is preserved throughout. Trouble comes from per-component slopes chosen by eye: two elements with different slopes will swap visual hierarchy at whatever width their lines intersect. Publishing the steps as custom properties makes that discipline enforceable.

saying these in an interview costs you the question

  • Dividing the target size by the viewport width to get the vw coefficient
  • Assuming the line passes through the origin, so no constant term is needed
  • Writing the intercept in px and calling the result accessible
  • Omitting the bounds because the maths already fits the design range
  • Picking the two anchor widths from specific phone and laptop models

context