ScreenshotNeo

BlogHow-to

Image With Text in HTML

Learn how to overlay live HTML text on an image, add accessible captions, choose alt text, and avoid common responsive and readability problems.

By the ScreenshotNeo team1 October 20268 min read

Use real HTML text in a positioned wrapper when words must appear over an image. Keep the image and text as separate elements, then use CSS positioning, contrast, and responsive rules to make the overlay readable. Use <figure> and <figcaption> when the text is a caption rather than an overlay.

This approach keeps the words selectable, searchable, resizable, and available to assistive technology. W3C WAI recommends styled actual text instead of image-based text when CSS can provide the presentation: Images of Text. For image alternatives, follow MDN’s <img> guidance.

1. Overlay text on an image with HTML and CSS

The basic pattern has three parts:

  1. A wrapper with position: relative.
  2. An image that fills the wrapper.
  3. A heading, paragraph, or link positioned over the image.
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Image with text overlay</title>
  <style>
    .hero {
      position: relative;
      isolation: isolate;
      max-width: 900px;
      margin: 2rem auto;
      overflow: hidden;
      border-radius: 1rem;
      background: #111;
    }

    .hero img {
      display: block;
      width: 100%;
      height: auto;
      aspect-ratio: 16 / 9;
      object-fit: cover;
    }

    .hero::after {
      content: "";
      position: absolute;
      inset: 0;
      z-index: -1;
      background: linear-gradient(
        90deg,
        rgb(0 0 0 / 0.72),
        rgb(0 0 0 / 0.18) 70%,
        transparent
      );
    }

    .hero__content {
      position: absolute;
      inset: auto auto 10% 6%;
      max-width: min(34rem, 88%);
      color: #fff;
    }

    .hero__content h1 {
      margin: 0 0 0.5rem;
      font-size: clamp(1.75rem, 4vw, 3.5rem);
      line-height: 1.05;
    }

    .hero__content p {
      margin: 0;
      font-size: clamp(1rem, 1.8vw, 1.25rem);
      line-height: 1.5;
    }

    @media (max-width:  fortyrem) {
      .hero__content {
        inset: auto 5% 7% 5%;
      }
    }
  </style>
</head>
<body>
  <main>
    <section class="hero" aria-labelledby="hero-title">
      <img
        src="mountain.jpg"
        alt="Sunrise over a mountain ridge"
        width="1600"
        height="900"
      >
      <div class="hero__content">
        <h1 id="hero-title">Plan your next trail</h1>
        <p>Routes, weather, and maps for a better day outdoors.</p>
      </div>
    </section>
  </main>
</body>
</html>

Replace mountain.jpg and the text with your content. The wrapper establishes the positioning context, while inset places the text near the lower-left corner. The gradient is a contrast aid; it is not a substitute for checking the actual image at every crop and viewport.

Center the text

.hero__content {
  position: absolute;
  top: 50%;
  left: 50%;
  width: min(90%, 36rem);
  transform: translate(-50%, -50%);
  text-align: center;
}

Put text at the bottom with a solid panel

.hero__content {
  position: absolute;
  right: 1rem;
  bottom: 1rem;
  left: 1rem;
  padding: 1rem 1.25rem;
  color: #fff;
  background: rgb(0 0 0 / 0.7);
  border-radius: 0.5rem;
}

2. Add a semantic caption with <figure>

A caption describes or contextualizes a figure for everyone who can see it. It is different from overlaid marketing copy and different from alternative text. MDN recommends <figure> and <figcaption> when an image needs a caption: HTML images.

<figure>
  <img
    src="chart.png"
    alt="Line chart showing sign-ups rising from January through June"
    width="1200"
    height="675"
  >
  <figcaption>Sign-ups increased steadily during the first half of the year.</figcaption>
</figure>

Style the caption normally, or place it visually over the image while retaining the semantic relationship:

.figure-overlay {
  position: relative;
  max-width: 48rem;
}

.figure-overlay img {
  display: block;
  width: 100%;
  height: auto;
}

.figure-overlay figcaption {
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  padding: 0.75rem 1rem;
  color: #fff;
  background: rgb(0 0 0 / 0.72);
}

3. Choose the right alt text

The alt value is a text replacement for the image’s content or function. It can be read by screen readers and may appear when the image cannot load. Write the essential subject or action, not a file name or a visual inventory. See W3C WAI’s Images Tutorial.

Image purpose Recommended markup Example
Informative image Describe the important content in alt alt="Sunrise over a mountain ridge"
Functional image link Describe the destination or action alt="Open the trail map"
Decorative background Use CSS background or alt="" <img src="texture.png" alt="">
Text-heavy image Keep the words in HTML; if unavoidable, include the same words in the alternative Use a real heading or paragraph whenever possible

Do not duplicate a caption word for word in alt unless the image itself needs that exact replacement. A visible caption supplies context; alt supplies a substitute when the image is unavailable. The title attribute is not a replacement for either one.

4. Make overlays readable and responsive

  • Contrast: Add a gradient, scrim, or opaque panel behind text. Check the darkest and lightest parts of every image.
  • Scalable type: Use relative units or clamp(), and allow headings to wrap.
  • Safe areas: Keep text away from faces, controls, and the image edges.
  • Short copy: Long paragraphs belong below the image; overlays work best for a heading, short description, or action.
  • Small screens: Test narrow widths. If the image becomes too busy, move the text into a normal block below it.
  • Keyboard access: If the overlay contains a link or button, keep it a real interactive element with a visible focus style.
  • Reduced motion: Avoid hover-only animations that hide or reveal essential words.
@media (max-width: 40rem) {
  .hero {
    overflow: visible;
    background: transparent;
  }

  .hero img {
    border-radius: 0.75rem 0.75rem 0 0;
  }

  .hero__content {
    position: static;
    max-width: none;
    padding: 1rem;
    color: #111;
    background: #fff;
    border-radius: 0 0 0.75rem 0.75rem;
  }

  .hero::after {
    display: none;
  }
}

5. Overlay text versus text inside the image file

Approach Use it for Trade-offs
Live HTML text positioned with CSS Headings, labels, calls to action, and other page content Selectable, searchable, resizable, and adaptable to user color settings
<figure> and <figcaption> Visible context or explanation for a figure Provides semantic association; it does not replace useful alt text
Text baked into a bitmap Artwork where the lettering is part of the design Harder to resize and translate; the alternative must include the represented words when they matter

6. Common errors and fixes

Problem Cause Fix
Text appears beside the image The containing element is not positioned Add position: relative to the wrapper and position: absolute to the overlay.
Text is behind the image Stacking order or a positioned pseudo-element covers it Set an explicit stacking context with isolation: isolate and suitable z-index values.
Overlay moves when the image changes size The text is positioned relative to the page, not the image wrapper Keep image and text inside the same relative wrapper.
Words are unreadable Insufficient contrast or busy imagery Use a scrim, opaque panel, shorter copy, or a different crop.
Text is cut off on phones Fixed dimensions or no wrapping Use width: 100%, clamp(), flexible padding, and a mobile layout.
Caption is not associated with the image A plain div was used Wrap the content in <figure> and use <figcaption>.
Screen reader announces meaningless content Decorative imagery has descriptive alt text Use alt="" for purely decorative images.
Important words disappear when images are disabled Words were baked into the bitmap Move the copy into HTML and style it with CSS.
Click target is difficult to use A tiny link is placed over a changing image Use a generous button or link area, visible focus, and a non-overlay fallback.

7. Performance and reliability checklist

  • Set intrinsic width and height (or an aspect-ratio) to reduce layout shifts.
  • Use an appropriately sized image and modern formats such as WebP or AVIF when your delivery pipeline supports them.
  • Use loading="lazy" for below-the-fold images; load the primary hero image according to its importance.
  • Keep critical overlay text in the initial HTML so it does not depend on a client-side image request.
  • Test with images blocked, high zoom, keyboard navigation, forced colors, and a narrow viewport.
  • Do not put essential instructions only in a background image or hover state.

8. Capture the finished HTML as an image

If you need a screenshot for documentation, visual regression, social cards, or a report, you can run a browser yourself with Playwright or Puppeteer. That gives you control over viewport, waits, fonts, and selectors, but you must operate the browser and handle consent banners, popups, failed loads, and retries.

Minimal Playwright example

import { chromium } from 'playwright';

const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
await page.goto('http://localhost:3000/image-with-text.html', {
  waitUntil: 'networkidle'
});
await page.screenshot({ path: 'image-with-text.png', fullPage: true });
await browser.close();

Wait for a specific image or font when it matters more than general network idle:

await page.goto(url, { waitUntil: 'domcontentloaded' });
await page.locator('.hero img').waitFor({ state: 'visible' });
await page.evaluate(() => document.fonts.ready);
await page.screenshot({ path: 'hero.png', clip: await page.locator('.hero').boundingBox() });

Or skip the browser setup

ScreenshotNeo returns a PNG, JPEG, WebP, or PDF from one GET request. It can wait for a selector, delay, or network idle; capture full pages or one CSS-selected element; run custom CSS and JavaScript; choose device presets, viewport, dark mode, and retina scale; and hide selectors before capture. Cookie banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, timeouts, failed loads, and cache hits are never billed.

Read the ScreenshotNeo API documentation for all options. The simplest calls are:

cURL

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://stripe.com \
  -o shot.webp

Python

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const bytes = Buffer.from(await res.arrayBuffer());
require('node:fs').writeFileSync('shot.webp', bytes);

Responses include X-Page-Verdict and X-Billed headers, so your job can distinguish a clean capture from a failed or non-billable result. ScreenshotNeo also provides bulk capture for up to 100 URLs per call, caching with a TTL you choose, signed links for public image tags, asynchronous jobs with signed webhooks, a usage API, and an MCP server with take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. Plans include 1,000 free shots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

9. FAQ

Should overlay text be inside the img tag?

No. An img element cannot contain child text. Put the image and a separate text element in a positioned wrapper.

Can I use a CSS background image?

Yes for decorative imagery. If the image conveys information, an <img> with appropriate alternative text is usually clearer and more accessible.

Is figcaption required for every image?

No. Use it when a visible caption adds context. It is not required for every informative or decorative image.

How do I keep an overlay readable over any photo?

Use a contrast-tested solid panel or gradient, constrain the copy length, and provide a mobile layout that moves the text below the image when necessary.

Can search engines read the overlay?

Yes, when it is ordinary HTML text. Text embedded only in a bitmap is less adaptable and should have an equivalent alternative when it carries meaning.