CSS Redacted Text

Sensitive text blurred by default and revealed on hover using CSS filter: blur(), with no JavaScript.

Published August 12, 2026 Beginner 7 min read

CSS redacted text blurs a run of words with filter: blur() and clears the blur on :hover, so a document can show its structure without showing its contents. The whole effect is two declarations on an inline <span> plus a transition to smooth the change between the two states. Nothing is fetched and no script runs.

The pattern turns up on spoiler tags, screenshots in documentation, and previews of paid content. Three versions appear on this page: the blur on hover below, a solid blackout bar in the style of a printed censor mark, and a checkbox toggle that reveals every hidden run at once for readers who cannot hover.

Blur, then reveal on hover

Project Nightfall is our internal codename for the upcoming acquisition of Meridian Labs. The deal is expected to close on March 14th for a total of $240 million.

The lead negotiator is Sarah Chen from our legal team. Her direct contact is sarah@example.com . Please keep this confidential until the public announcement.

// hover to reveal

HTML

<div class="redacted-block">
  <p>Project <span class="redacted">Nightfall</span> is our internal codename for the upcoming acquisition of <span class="redacted">Meridian Labs</span>. The deal is expected to close on <span class="redacted">March 14th</span> for a total of <span class="redacted">$240 million</span>.</p>
  <p>The lead negotiator is <span class="redacted">Sarah Chen</span> from our legal team. Her direct contact is <span class="redacted">sarah@example.com</span> . Please keep this confidential until the public announcement.</p>
  <span class="hint">// hover to reveal</span>
</div>

CSS

.redacted-block {
  max-width: 400px;
  font-size: .9rem;
  line-height: 1.85;
  color: #f0f0f0;
}

.redacted-block p {
  margin: 0 0 1rem;
}

.redacted-block p:last-child {
  margin-bottom: 0;
}

.redacted {
  display: inline;
  filter: blur(5px);
  cursor: pointer;
  transition: filter .4s ease;
  user-select: none;
  color: #f0f0f0;
  border-radius: 3px;
  padding: 0 .1em;
}

.redacted:hover {
  filter: blur(0);
}

.hint {
  font-family: "DM Mono", "Fira Code", Consolas, monospace;
  font-size: .7rem;
  color: #88888f;
  display: block;
  margin-top: 1.25rem;
  letter-spacing: .04em;
}

Other ways to build it

Blackout bar

No blur at all. The run gets a background in the text color and color: transparent, so it paints as a solid rectangle. This hides more than a blur does, because every redaction becomes the same featureless block and word shape stops leaking. Both properties transition back together on hover.

Wire the retainer to account 4417 8802 9931 at routing code 021000021 before the close of business on Thursday.

// hover a bar to reveal

HTML

<div class="redacted-block">
  <p>Wire the retainer to account <span class="redacted-bar">4417 8802 9931</span> at routing code <span class="redacted-bar">021000021</span> before the close of business on <span class="redacted-bar">Thursday</span>.</p>
  <span class="hint">// hover a bar to reveal</span>
</div>

CSS

.redacted-bar {
  background: #f0f0f0;
  color: transparent;
  border-radius: 2px;
  padding: 0 .2em;
  cursor: pointer;
  -webkit-user-select: none;
  user-select: none;
  transition: background .3s ease, color .3s ease;
}

.redacted-bar:hover {
  background: transparent;
  color: #f0f0f0;
}

Reveal everything with one toggle

A hidden checkbox in front of the paragraph holds the revealed state, and the general sibling combinator reaches every redaction after it. Because the control is a real checkbox behind a label, it takes focus and responds to the space bar, which hover never does. Hover is switched off inside this block so the toggle is the only way in.

The lead negotiator is Sarah Chen and the deal is valued at $240 million, closing on March 14th.

HTML

<div class="redacted-block redacted-switchable">
  <input type="checkbox" id="reveal-1" class="redacted-check">
  <label for="reveal-1" class="redacted-switch">Reveal redactions</label>
  <p>The lead negotiator is <span class="redacted">Sarah Chen</span>.</p>
</div>

CSS

.redacted-check {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}

.redacted-switch {
  display: inline-block;
  margin-bottom: .9rem;
  padding: .3rem .7rem;
  border: 1px solid #2a2a2d;
  border-radius: 999px;
  font-size: .75rem;
  color: #b8ff57;
  cursor: pointer;
}

/* ~ reaches every later sibling, so one checkbox clears the paragraph */
.redacted-check:checked ~ p .redacted {
  filter: blur(0);
}

/* the input is visually hidden, so the focus ring goes on the label */
.redacted-check:focus-visible + .redacted-switch {
  outline: 2px solid #b8ff57;
  outline-offset: 3px;
}

/* the toggle is the trigger here, so hover must not also reveal */
.redacted-switchable .redacted:hover {
  filter: blur(5px);
}

@media (prefers-reduced-motion: reduce) {
  .redacted { transition: none; }
}

How it works

filter: blur() is applied to inline <span> elements wrapping the sensitive words. The transition property animates the blur back to 0 on :hover. user-select: none stops the blurred text being dragged into a selection and read from the clipboard. The words themselves stay in the HTML, so this hides text from a casual reader, not from anyone willing to open the page source.

filter: blur(5px) runs a Gaussian blur over the element after it is painted. The length is the standard deviation of the blur kernel, not a pixel radius, so the visible smear reaches roughly two to three times that value in every direction. At 5px against text set near 0.9rem, letterforms stop resolving but the block keeps its length and its ascenders, which is why a blurred name still reads as a name.

Any filter value other than none turns the element into a stacking context, and it also becomes the containing block for absolutely and fixed positioned descendants. On an inline <span> holding plain words that never matters. Put a blur on a wrapper that also holds a position: fixed overlay and the overlay will suddenly anchor to the wrapper instead of the viewport, which is a hard bug to trace back to a blur.

user-select: none is doing real work here. Without it, a reader drags across the blurred run, copies it, and pastes the text in full, because the blur is a paint operation and the DOM underneath is untouched. Safari still needs -webkit-user-select alongside the unprefixed property. Even with both, view source and the accessibility tree still expose the words, so anything genuinely sensitive has to be removed on the server before the HTML is sent.

transition: filter .4s ease interpolates the blur radius between 5px and 0. Blur is one of the more expensive filters because the browser has to re-rasterize the element on every frame at a new kernel size. One or two runs animating at a time is fine. A page that blurs several dozen elements and transitions them together will drop frames on a low-end phone, and the fix is to shorten the transition or drop it entirely rather than to reduce the radius.

The same compositing pipeline drives mix-blend-mode, which combines an element with what sits behind it instead of processing it in isolation. Both are painted after layout, so neither one changes the size or position of anything, and both create stacking contexts as a side effect. A related hover pattern, the CSS hover tooltip, hides its content in a pseudo-element instead, which keeps the hidden string out of the normal text flow.

CSS properties used

filter
blur(5px) applies a Gaussian blur to the painted element. Setting it back to blur(0) on :hover reveals the text. Any non none value creates a stacking context.
transition
Animates the filter between the hidden and revealed states. Without it the text snaps from blurred to sharp with no intermediate frames.
user-select
none blocks the blurred run from being selected and copied. Safari needs the -webkit- prefixed form as well.
cursor
pointer signals that the run responds to interaction. A blurred span with a text cursor reads as a rendering fault rather than a control.
color
Paired with a matching background-color, transparent produces the solid blackout bar variant without any blur at all.

Browser support

FeatureChromeFirefoxSafariEdge
filter1835679
transition455.112
user-select423.112
prefers-reduced-motion746310.179

Figures come from Can I Use for CSS Filter Effects, CSS Transitions, user-select: none and the prefers-reduced-motion media query. Can I Use still records Safari as needing -webkit-user-select, so write both declarations. Filters shipped behind -webkit-filter in older Chrome and Safari, but every release in current use accepts the unprefixed property.

Accessibility notes

A hover trigger reaches neither keyboard nor touch. On a phone the first tap fires a synthetic hover, the word unblurs, and it re-blurs as soon as anything else is touched, which reads as a glitch rather than a control. The checkbox variant below is focusable and operates on the space bar, so it is the version to use when the hidden text is content the reader needs rather than a decorative spoiler.

Blur changes nothing in the accessibility tree. A screen reader announces the redacted words in full, at all times, in every variant on this page. That is the correct behavior for a spoiler warning, where the reader chose to visit, and the wrong behavior for anything confidential. If the text must not reach assistive technology, do not send it to the browser.

Blurred text fails contrast checks by construction, because there are no crisp edges left to measure. Keep the revealed state at the same contrast as surrounding body copy so the reader is not moving between a legible line and a dim one, and give the run a visible affordance, such as the underline or the tinted background used below, so a reader who cannot see the blur still knows something is there.

What you can build with it

  • Spoiler text. Plot points and puzzle answers in a review or a forum post, hidden until the reader decides to look.
  • Documentation screenshots. API keys, account numbers and customer names inside sample output, hidden in the page rather than edited out of an image.
  • Paywall previews. The first paragraph readable and the rest blurred, so the shape and length of the article are visible before signup.
  • Draft contract markup. Counterparty names and figures masked while a document is circulated internally for comment on wording.
  • Teaching examples. An exercise where the answer sits under a blur and the reader checks their own work by hovering.

Mistakes worth avoiding

  • Treating this as security. The words are in the HTML, in the DOM, and in the accessibility tree. Anyone can read them from view source without any special knowledge.
  • Leaving user-select off. A reader drags across the blurred run, copies it, and pastes the exact text, because the blur never touched the underlying characters.
  • Blurring a container that holds a position: fixed child. The filter makes the container the containing block, so the fixed child anchors to it and stops tracking the viewport.
  • Using a blur radius under about 3px on body copy. Word shapes and capital letters stay recognisable, so short strings like a first name or a two digit number can still be guessed.
  • Relying on hover alone. Touch and keyboard readers get no way in, and a reduced motion setting is ignored unless the transition is explicitly removed.

Frequently asked questions

Is CSS redacted text secure?
No. The text sits in the HTML unchanged, so view source, the DOM inspector and any screen reader all expose it. filter: blur() only affects painting. Treat it as a spoiler curtain for content the reader is allowed to see, and strip anything genuinely sensitive on the server.
Why can I still copy the blurred text?
user-select: none is missing, or it is present without the -webkit-user-select form that Safari still needs. Add both. Even then, copying is only blocked in the browser interface, not in the page source.
How do I make a black bar instead of a blur?
Set color: transparent and give the span a background-color in your text color, then swap both back on :hover. The blackout variant on this page uses exactly that, and it hides word length better than a blur because every run becomes a uniform rectangle.
Does redacted text work on mobile?
The blur renders correctly. The hover trigger does not, because mobile browsers fake a hover on first tap and clear it on the next touch anywhere else. Use the checkbox toggle variant, which responds to a real tap and to the keyboard.
What blur radius should I use?
Around three to five times the risk you are hiding. For a name or a short number, go past 6px, because a small radius leaves word length and capital letters readable. For long runs of prose, 4px to 5px is enough to stop anyone reading it at a glance.