AC Actual Notes
Subscribe
Design Systems

Designing with quiet defaults

A typography system should improve readability without taking over app UI. The strongest defaults are the ones you stop noticing.

AM Ada Meridian · Senior designer 8 min read Typography

When a framework ships a typography system, the temptation is to make it loud: a seven-step type scale, a fluid ramp, a Bootstrap-style utility for every heading level. The result is a system that wants to be everywhere — and a UI that ends up shaped by accident.

The quieter choice is to make the global baseline do almost nothing. The baseline should give readable defaults for plain HTML, leave the visual hierarchy to the components and the prose scope, and avoid exposing more tokens than theme work has earned.

Why the baseline should stay mild

The first reason is reach. A global h1 rule touches a card title in a sidebar, a dialog heading, a section label in a dashboard, and a hero banner on a marketing page. If the rule says large, every one of those becomes large. If the rule says inherit, the component keeps its own design.

The second reason is rhythm. A typography system that scales every heading by viewport width is great on a hero and miserable in a form. The decision to scale is a decision about surface — and that decision belongs to the surface, not to the framework.

The three scopes, from broadest to narrowest: baseline, .prose, optional fluid module.

Three scopes, three jobs

A typography system stays small when it has clear boundaries. The framework this article ships with uses three.

  • Baseline — applies everywhere. Owns the minimum needed to make plain HTML readable: a tight line-height on headings, a text-wrap: balance where supported, the hr rule, the ::selection rule, the code font family, the small font size.
  • .prose — opt-in rich text scope. Owns the visual hierarchy of authored content: heading sizes, weights, rhythm, link affordance, list spacing, blockquote and figure treatment, and table styling for prose tables.
  • Optional fluid module — not imported by default. Exposes --fluid-display, --fluid-title, and --fluid-lead, plus the composed .text-display, .text-title, and .text-lead classes. For landing pages, hero sections, and documentation homepages.

A small example

Compare a global heading reset to a scoped one. The first is loud; the second stays out of the way.

/* Loud: shapes app UI */
  h1, h2, h3, h4, h5, h6 {
    font-size: var(--fs-1);
    font-weight: 700;
    line-height: 1.15;
    margin-block: 1.5em 0;
  }

  /* Quiet: scoped to prose, leaves app UI alone */
  :where(h1, h2, h3, h4, h5, h6) {
    color: var(--heading, inherit);
    line-height: var(--line-height-tight);
  }

  @supports (text-wrap: balance) {
    :where(h1, h2, h3, h4, h5, h6) {
      text-wrap: balance;
    }
  }

The second version does two things and stops. The first version does four things and reaches into every card, dialog, and table heading in the app.

What it means in practice

In practice, "quiet defaults" means a few concrete choices that compose well.

  1. Global a inherits color. .prose a gets the underline. Components that need link semantics style themselves.
  2. Global headings get a tight line-height and text-wrap: balance where supported — nothing more. Size and weight live in .prose or in the component.
  3. The hgroup element is structural only: a grid, a gap, a margin reset. No font-size, no color.
  4. No transition: all. Transitions target specific properties.
  5. No --font-weight-light. Light weights depend on the typeface and rarely make a good default.
  6. No text-align: center hidden inside .text-balance. Balance is about wrapping, not alignment.

When the rule applies

The split between scopes is not just a code organization. It maps to a recurring decision: is this content authored, or is it UI?

Surface Scope Reason
Article, blog post, docs .prose Authored content benefits from heading hierarchy, links, lists, and figure treatment.
Card title, dialog title Component Component owns its own typography; .prose would over-shape it.
Hero banner, landing page Optional fluid module Display text should scale with the viewport — and only here.
Nav link, breadcrumb, tab Component Link semantics are owned by the component, not by .prose.

The table is also a useful sanity check. If the same surface would feel right in both an app shell and a marketing page, the typography probably belongs to the component, not to a global rule.

Good defaults should be quiet, not invisible. The user feels the system when it is absent — because nothing is fighting the design.

What we left out

The system above does not include a public type scale. There is no fs-1 to fs-7, no heading-level utility class, no text-h1 ladder. We resisted because adding a public scale is a contract, and we want theme work and real component use to earn the contract before locking it in.

When a scale is needed, the optional fluid module is the right place to start. A future theme can override its tokens, not invent a parallel scale inside components.

Closing

A typography system earns its keep by being invisible. The user does not think about the line height of a heading. They do not notice the wrap balance on a paragraph. They read, they scan, they move on.

The framework's job is to be the thing that lets the user do that — and to stay out of the way when the component, the theme, or the page wants to do something specific. Quiet defaults is not a stylistic choice. It is the surface that the rest of the system composes on.

Share
AM
Ada Meridian

Senior designer working on design systems, typography, and quiet interfaces. Writes about the boring parts that make the interesting parts possible.

3 thoughts

Join the conversation — comments are open for two weeks.

  1. JL
    Jana Lindgren

    The split between .prose and component-level typography is what made this click for me. We were fighting prose for a year.

  2. RM
    Rin Moreau

    Curious how this holds up with right-to-left content. The text-wrap: balance part especially.

  3. AM
    Ada Meridian Author

    @Rin — text-wrap: balance follows the inline direction by spec, so it works the same in RTL. The optional fluid module uses logical properties too. Good question.

Leave a thought

Markdown is supported. Be kind.

More on the quiet parts of design systems.