← Journal

TYPOGRAPHY · · 5 min read

Ghost Text and Restraint: Using Oversized Type Without Shouting

Ghost text should be felt before it is read, and this guide covers the stroke and fill treatments, opacity ranges, and accessibility checks that keep it in the background.

A single oversized word behind a hero section can give a page scale and a point of view. Pushed too far, it competes with the headline, muddies the body copy, and leaves screen readers repeating decoration.

The technique works when the type is felt rather than read. Here is how we set it up in the Crystal Design System, and the checks we run before it ships.

What ghost text is for

Ghost text is a structural device. It adds depth and rhythm to a layout that would otherwise be flat, and it is never the message.

If removing it changes what the page says, it has stopped being ghost text and become a headline. Everything below follows from that rule.

The base style

Our ghost type is large, heavy, and tightly set. Weight and size carry the presence, so opacity can stay very low.

.ghost {
  font-family: Urbanist, system-ui, sans-serif;
  font-size: clamp(140px, 18vw, 280px);
  font-weight: 700;
  letter-spacing: -0.04em;
  line-height: 0.85;
  pointer-events: none;
  user-select: none;
}

The tight line height lets multi-line ghost text stack as one block, and the negative tracking keeps the letterforms close enough to read as a single shape.

Stroke versus fill

There are two basic treatments, and the choice changes how the text sits on the page.

/* Stroke: default */
.ghost--stroke {
  color: transparent;
  -webkit-text-stroke: 1.5px rgba(0, 0, 0, 0.055);
}

/* Heavy stroke: larger scale, more emphasis */
.ghost--stroke-heavy {
  color: transparent;
  -webkit-text-stroke: 2px rgba(0, 0, 0, 0.08);
}

/* Fill: quieter, light backgrounds */
.ghost--fill {
  color: rgba(0, 0, 0, 0.035);
}

Stroke reads as an outline. It keeps the interior of the letters empty, so content passing over it sees almost no tonal change. It suits sections where cards or images overlap the type.

Fill reads as a tint. It is the softest option and works well behind plain text blocks, but it does shift the background tone slightly everywhere the letters sit.

On dark sections, flip the base color to white and keep the values low: 3.5% for fill and 5.5% for stroke. Dark backgrounds make light marks more visible, so resist the urge to raise them.

The opacity range

Our working range is roughly 3.5% to 8%. Fill sits at the low end, stroke in the middle, and heavy stroke at the top.

The test is simple. Squint, or step back from the screen. You should sense a shape, and you should have to look deliberately to read the word. If it reads at a glance, lower the value.

Check on more than one display. A value that is barely there on a calibrated monitor can vanish on a cheap panel, and one that looks perfect on a dim laptop can glare on a bright external screen. Aim for a result that degrades gracefully in both directions, so that if the ghost disappears entirely the page still works.

Keep it out of the accessibility tree

Ghost text is decoration, so mark it that way. Otherwise assistive technology will read a giant word with no context.

<section class="hero">
  <span class="ghost ghost--stroke" aria-hidden="true">Studio</span>
  <h2>What we build</h2>
</section>

Avoid generating ghost text from a CSS pseudo-element with content. Some screen readers announce generated content, and you lose the ability to hide it with an attribute.

Two more safeguards belong in the stylesheet:

.hero { position: relative; overflow: hidden; }

@media (forced-colors: active), print {
  .ghost { display: none; }
}

The overflow rule stops oversized type from creating horizontal scroll. The media query removes a decorative layer in modes where color is overridden or ink is expensive.

Contrast checks that matter

Ghost text itself is decorative, so it does not need to meet text contrast ratios. The real content on top of it does, and the ghost layer can erode that margin.

  1. Measure real text against the background with the ghost layer present, not the clean page color.
  2. Check the worst spot, where a letter edge or fill sits directly behind a line of body copy.
  3. Hold body text to 4.5:1 and large text to 3:1, per WCAG AA.

Our secondary text color, #6B6B6B, clears 4.5:1 on the page background without much room to spare. A fill-style ghost behind it narrows that margin further, so measure it. Tertiary text (#9A9A9A) falls below 4.5:1 even on a clean background, so keep it off any area where ghost text sits.

When a check fails, change the layout before touching the type. Move the ghost text off the copy, lower its opacity, or switch from fill to stroke.

Common mistakes

  • Raising opacity to make it “visible.” If it needs to be seen, it is content. Make it a heading.
  • Placing it directly behind paragraphs. Position it in margins, section edges, or behind large headings, away from long reading.
  • Using several ghost words in one viewport. One is atmosphere, and two or more start to look like wallpaper.
  • Leaving it readable to assistive tech. Without aria-hidden, the decoration becomes noise.
  • Skipping the stroke check on variable fonts. Overlapping contours can show as artifacts at joins with -webkit-text-stroke. Inspect at full size.
  • Mixing typefaces. Ghost text uses the same family as the page. A second sans-serif just creates visual conflict.

The principle

Restraint is what makes oversized type work. Set the weight and scale to carry the shape, keep the opacity low enough that it nearly disappears, and verify that nothing in front of it suffers.

If you are building a system where decisions like these need to hold across several products, we would be glad to talk through how we approach it in the Crystal Design System.