# Mixed Font Families, Weights, and Sizes

**SHOULD** · **ID:** `design-rams-font-consistency` · **Category:** design
**Source:** [RAMS](https://www.rams.ai/)
**Interactive version:** https://ui-guides-agent-rules.netlify.app/principles/design-rams-font-consistency

> SHOULD: Use consistent font families, weights, and sizes throughout the interface. Limit to 2-3 font families maximum; take sizes from one typographic scale, with body text >= 16px on web and nothing below 12px.

Hold the whole type system to one decision: a small set of families, a named weight per role, and a single scale for sizes

That six-word bullet under "Typography" is the entire upstream rule, and it names all three axes at once — families, weights, sizes — so this principle does too. (It was previously split across two entries; the split invented a distinction the source never made.) The explanation is ours. FAMILIES: every extra family is a new voice in the room, and readers do not hear "variety", they hear "assembled from parts". Two is a system — one display, one text; a third needs a reason, usually a mono for code. WEIGHTS: give each weight a job (regular for body, medium for labels, semibold for headings) instead of nudging one paragraph to 500 because it looked thin. SIZES: a type scale is the index of the page — a reader who meets 15px, 16px, and 17px body copy on one screen cannot tell whether the difference means anything, so they stop trusting size as a signal at all. Pick the scale (Tailwind's xs → 2xl is one), give each step a job, and never introduce a size to solve a one-off fitting problem; fix the layout instead. The common thread across all three: reach for weight and size WITHIN one family before you reach for a second family, and take every value from a named token rather than from a style attribute.

## Bad — do not do this

`design-rams-font-consistency-bad`

```tsx
/**
 * Bad: all three axes of the upstream bullet fail at once — four font families, a
 * weight picked per paragraph, and four sizes that belong to no scale. Each choice
 * looked reasonable in isolation; together the card has no typographic system.
 */
export function RamsFontConsistencyBad() {
  return (
    <div className="w-full max-w-sm space-y-4">
      <div className="rounded-lg border border-border bg-card p-4">
        <h4 className="mb-3 font-medium">Release notes</h4>

        <article className="space-y-2 rounded-lg bg-muted p-4">
          {/* Serif italic display face, off-scale size */}
          <h5 className="font-serif italic" style={{ fontSize: '19px', fontWeight: 600 }}>
            Version 4.2
          </h5>

          {/* Sans body — but a hand-picked 15px and a nudged weight */}
          <p className="font-sans" style={{ fontSize: '15px', fontWeight: 450 }}>
            Body copy in the sans family, at a size that exists nowhere else in the product.
          </p>

          {/* Mono, for no reason, at yet another size */}
          <p className="font-mono" style={{ fontSize: '13px', fontWeight: 500 }}>
            This paragraph is monospace because it happened to look tidy.
          </p>

          {/* A fourth family, arrived at by copy-paste */}
          <p style={{ fontFamily: 'cursive', fontSize: '11px', fontWeight: 700 }}>
            And the fine print picked up a fourth family entirely.
          </p>
        </article>

        <div className="mt-3 space-y-1 rounded bg-muted p-2 font-mono text-xs">
          <code className="block text-error">families: serif, sans, mono, cursive</code>
          <code className="block text-error">weights: 600, 450, 500, 700</code>
          <code className="block text-error">sizes: 19px, 15px, 13px, 11px</code>
        </div>
      </div>

      <p className="text-xs text-error">
        Four voices, four weights, four sizes off any scale. The reader cannot tell which
        differences are meaningful, so size and weight stop working as signals at all &mdash;
        and every value is a style attribute, so no token can ever fix it centrally.
      </p>
    </div>
  );
}
```

## Good — do this

`design-rams-font-consistency-good`

```tsx
/**
 * Good: one family carries the prose (mono is reserved for code, which is a reason),
 * one named weight per role, and every size comes from the scale. The range is found
 * with weight and size WITHIN one family, rather than by reaching for a second family.
 */
export function RamsFontConsistencyGood() {
  return (
    <div className="w-full max-w-sm space-y-4">
      <div className="rounded-lg border border-border bg-card p-4">
        <h4 className="mb-3 font-medium">Release notes</h4>

        <article className="space-y-2 rounded-lg bg-muted p-4 font-sans">
          {/* Heading: a scale step + the heading weight */}
          <h5 className="text-lg font-semibold">Version 4.2</h5>

          {/* Body: base step, regular weight */}
          <p className="text-sm font-normal">
            Body copy at the base step. Hierarchy comes from weight and size inside one
            family, so nothing has to change voice in order to change emphasis.
          </p>

          {/* Label: same size as body, one weight step up — emphasis without a new family */}
          <p className="text-sm">
            <span className="font-medium">Breaking:</span> the label is emphasised with weight,
            not with a different typeface.
          </p>

          {/* Mono earns its place: it marks code, which is what mono is for */}
          <code className="rounded bg-background p-1 font-mono text-xs">npm i pkg@4.2.0</code>
        </article>

        <div className="mt-3 space-y-1 rounded bg-muted p-2 font-mono text-xs">
          <code className="block">families: font-sans (+ font-mono for code)</code>
          <code className="block">weights: normal / medium / semibold</code>
          <code className="block">sizes: text-lg, text-sm, text-xs</code>
        </div>
      </div>

      <p className="text-xs text-success">
        One family carries the prose; weight names the role; size comes from the scale. Each
        step has a job, so a reader who sees a difference can trust that it means something.
      </p>
    </div>
  );
}
```

## References

- [Rams design review skill](https://rams.ai/rams.md)
- [Tailwind: font-family](https://tailwindcss.com/docs/font-family)
- [Tailwind: font-size](https://tailwindcss.com/docs/font-size)
- [Tailwind: font-weight](https://tailwindcss.com/docs/font-weight)
