SVG✨ Premium

Image Mask Text

An image that only shows through the letters of a heading and drifts slowly inside them: your photo in a CSS variable, the text stays real text.

CSSBlendText

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. SVG has 30 effects, including 3 free. Explore the category →

3 usage examples

Island name across the page, the sea inside the letters — cruise agency — Image Mask Text example 1

① Island name across the page, the sea inside the letters — cruise agency

WhenThe top of a port-of-call page: navigation, “3-day stop · Aegean Sea”, then “OMBRISA” across the full width, filled with a view of the island and the sea. At the bottom: departure date, number of nights, price and a “Book” button.
WhyThe place name and its picture become one image: you read the destination and see it at once, with no separate photo banner. The vertical glide takes the eye from the sky to the beach and the sea.
Settings--imt-size:104px, --imt-width:100%, --imt-min-h:0px, --imt-card:transparent, --imt-page:transparent, --imt-height:0px; path --imt-from:50% 35% → --imt-to:50% 78% over --imt-speed:7s; --imt-image = SVG drawing of the island (a photo in production).
First name filled with the chosen pattern — custom schoolbag label — Image Mask Text example 2

② First name filled with the chosen pattern — custom schoolbag label

WhenA schoolbag shop's configurator: on the left the label “This schoolbag belongs to Léonie”, on the right the name field and four patterns (stripes, dots, waves, checks). Each click on a pattern fills the name with it; until someone clicks, the scene cycles through them.
WhyThe preview shows the product as it will be printed: the pattern inside the letters, not as a thumbnail next to them. Switching images is just changing a variable, with no reload and no redrawing of the text.
Settings--imt-size:66px, --imt-width:100%, --imt-card:transparent; the scene script runs root.style.setProperty('--imt-image', …) with the clicked button's image (SVG data-URI patterns), buttons with aria-pressed.
“1,200” filled with a forest — a charity's annual report — Image Mask Text example 3

③ “1,200” filled with a forest — a charity's annual report

WhenPage 4 of a reforestation charity's report: “Trees planted”, then “1 200” at 92 px filled with a forest, left-aligned, with its explanatory sentence; on the right, three bar indicators (survival, area, species).
WhyThe report's key figure also carries its subject: you read a quantity and see trees. On light paper the deep forest greens give a strong contrast with no added colour.
Settings--imt-size:92px, left alignment (justify-content:flex-start on the root, text-align:left on the text, in the scene CSS); diagonal path --imt-from:30% 30% → --imt-to:70% 90%, --imt-speed:10s; forest drawn in SVG.

How it works

The .imt-text heading gets the image as its background (background-image:var(--imt-image)), framed with cover on the text box. background-clip:text (and its -webkit- version) cuts that background to the exact letter shapes, and -webkit-text-fill-color:transparent removes the normal fill: only the image remains, wherever there are letters.

The imtGlide animation moves the background position from --imt-from (50% 10%) to --imt-to (50% 90%) over 8 s, then back (alternate, ease-in-out): like a camera slowly tilting down the photo. With cover framing the image overflows the box in one direction only; for a wide heading that is the height, hence the vertical path by default.

Your image goes in the HTML, on the root: style="--imt-image:url('photo.jpg')". Left as is, a sunset over the sea drawn in SVG (embedded in the CSS, no file) fills the text. The text is weight 900 at 4rem (Inter when available, otherwise the system font), centred in a 280 × 180 px dark card that --imt-card, --imt-width and --imt-min-h can hide or stretch. CSS only, no JavaScript.

Accessibility

  • Real text: the heading stays an <h2> (or any element with the imt-text class): read by screen readers, selectable, indexed. The image is only a background: if it carries information, also say it in nearby text.
  • Contrast: legibility depends on the photo colours under the letters. A pale sky area on a light page, or a shadow on a dark one, erases parts of letters. Set the --imt-from/--imt-to path to stay in a contrasted area, and use a very heavy font.
  • Reduced motion: under prefers-reduced-motion: reduce the animation is removed and the image settles in the centre (50% 50%).
  • Fallback: should background-clip:text be unsupported, the text shows in light violet (color:#c9a3f5) instead of vanishing.

Browser compatibility

background-clip:text (prefixed -webkit- for Chrome before 120 and Safari before 14), -webkit-text-fill-color, CSS variables inside @keyframes, SVG data-URI image. Measured in Chromium, Firefox and WebKit (Playwright): image visible in the letters and only there (hiding the heading removes the coloured pixels), glide, two copies, reduced-motion stop, 390 px. The image fill works in older versions (Chrome 49, Firefox 49, Safari 9.1); the versions below also honour reduced motion.

Chrome 74+✓ Full (measured)
Firefox 63+✓ Full (measured)
Safari 10.1+✓ Full (measured in WebKit)
Edge 79+✓ Full (Chromium engine)
Safari iOS 10.3+✓ WebKit engine, not verified on a real device
Chrome Android 74+✓ Full (Chromium engine)

Without clipping support the text stays readable in light violet. If the image fails to load the heading turns transparent: keep the image of your main heading on the same domain as the page.

The code

HTML structure to paste into your page (the CSS + JS come with your purchase):

index.html — structure
<div class="fx-0621-root">
  <div class="imt-card">
    <h2 class="imt-text">MASK</h2>
  </div>
</div>

One-time payment, no subscription — the code is yours forever

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Image (CSS — --imt-image) sunset over the sea (embedded SVG) url('photo.jpg') on the root, in the HTML (style) or your CSS. A bright, contrasted photo gives the most legible letters.
Text size (CSS — --imt-size) 4rem The bigger and bolder the text, the more recognisable the image.
Framing (CSS — --imt-fit) cover Background size. 160% auto enlarges the image and leaves room for a horizontal path.
Glide path (CSS — --imt-from, --imt-to) 50% 10% → 50% 90% Start and end positions of the image, back and forth. Two equal values stop the motion on that framing.
One-way duration (CSS — --imt-speed) 8s Longer = subtler motion; under 4 s the image races behind the text.
Card (CSS — --imt-card, --imt-width, --imt-min-h) #12121a / 280px / 180px transparent, 100% and 0px remove the card: the heading sits straight in your layout.
Block background and height (CSS — --imt-page, --imt-height) #0a0a0f / 300px transparent and 0px to fit the effect into an existing section.

FAQ

On the root: <div class="fx-0621-root" style="--imt-image:url('island.jpg')">. The image is framed with cover on the text box: a 1,200 px wide photo is enough for a full-width heading. Prefer an image on your own domain that loads fast: until it arrives the heading is transparent.
They fall on an area of the photo that matches the surrounding background (white sky on a white page, shadow on a black one). Move the path with --imt-from and --imt-to to a contrasted area, or set --imt-card to a card of the opposite colour.
Yes: root.style.setProperty('--imt-image', 'url(pattern.png)') swaps the image instantly without touching the text. To mirror a real button, read its image with getComputedStyle(button).backgroundImage and pass it as is.