rem vs em vs px: which unit should I use?

Short answer

Use rem for font sizes, spacing and layout, because it respects the reader's browser font-size setting. Use em when a value should scale with the element's own font size. Use px only for borders and shadow offsets.

#rem

Relative to the root font size — 16px by default, but larger if the reader has configured bigger text.

css
:root { --s-4: 1rem; --text-lg: 1.125rem; }

A layout built in rem grows with that preference. A layout built in px ignores it entirely, which is an accessibility failure for anyone who needs larger type.

#em

Relative to the current element's font size. Perfect for padding that should stay proportional to the text inside it:

css
.button {
  font-size: 1.125rem;
  padding: 0.6em 1.2em;    /* scales with the button's own text */
  border-radius: 0.4em;
}

Change the font size and the padding follows. That is exactly what you want for a component that comes in several sizes.

#px

Correct for things that should not scale:

css
border: 1px solid;
box-shadow: 0 1px 2px rgb(0 0 0 / 0.1);

A 1px hairline should stay a hairline. Scaling it to 1.5px produces a blurry line.

#ch and other useful ones

css
.prose { max-width: 65ch; }    /* ~65 characters — the readable line length */

ch is the width of the "0" glyph. It is the most direct way to control measure, which is one of the highest-impact typography settings there is.

Also worth knowing: vh/vw for viewport-relative sizing, and dvh for the dynamic viewport height that accounts for mobile browser chrome appearing and disappearing.