Text on dark image
A gradient scrim — a dark-to-transparent overlay — makes white text readable over any background image without fully obscuring the photo.
No scrim
Card title text
Hard to read on light areas
With scrim
Card title text
Legible on any photo
Quick implementation
/* Image card with gradient scrim for text legibility */
.image-card {
position: relative;
border-radius: 0.75rem;
overflow: hidden;
}
.image-card img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
}
/* Gradient scrim — dark at bottom where text sits, transparent above */
.image-card::after {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(
to top,
oklch(0 0 0 / 0.80) 0%, /* strong dark at the bottom */
oklch(0 0 0 / 0.50) 30%, /* medium through the text area */
oklch(0 0 0 / 0.10) 55%, /* fades out quickly */
oklch(0 0 0 / 0) 70% /* fully transparent at top */
);
}
/* Text sits above the scrim */
.image-card__content {
position: absolute;
z-index: 1; /* above the ::after scrim */
bottom: 0;
left: 0;
right: 0;
padding: 1.5rem 1rem 1rem;
color: white;
}
.image-card__title {
font-size: clamp(1rem, 2.5vw, 1.25rem);
font-weight: 700;
line-height: 1.2;
text-shadow: 0 1px 3px oklch(0 0 0 / 0.5); /* belt + suspenders */
}
.image-card__meta {
font-size: 0.875rem;
opacity: 0.85;
margin-top: 0.25rem;
}
Prompt this to your LLM
Includes role, constraints, framework variants, and edge cases.
You are a senior frontend engineer making text legible over
background photos on a card grid component.
Goal: Create a CSS gradient scrim technique that guarantees WCAG AA
contrast (4.5:1) for white text over any background photo.
Include:
- A ::after pseudo-element with linear-gradient to top (dark at
bottom where text lives, transparent at top)
- Use oklch(0 0 0 / opacity) for the dark stops — oklch black
is truly neutral and doesn't introduce color cast
- text-shadow as a secondary legibility technique (belt + suspenders)
- The multi-stop gradient trick to get a more natural-looking scrim
than a two-stop linear-gradient (which looks like a dark band)
Explain:
- Why a 2-stop black-to-transparent scrim looks unnatural
(Mach band illusion) and why using 4+ stops with eased values
produces a more organic falloff
- WCAG contrast ratio math: how dark does the overlay need to be
for white text to pass at 4.5:1 given a typical mid-range photo
Framework variant: Show how to use this in a Next.js Image component
alongside the blurDataURL placeholder.
Return only the CSS with inline comments.
Why 4 gradient stops look better than 2
A simple two-stop gradient from black 50% to transparent 100% creates a Mach band — a sharp perceptual edge where the gradient transitions feel sudden even though they're technically smooth. Using four or more stops with eased intermediate values produces a falloff that matches how light actually behaves. The values 0.80 → 0.50 → 0.10 → 0 at positions 0% → 30% → 55% → 70% approximate an ease-in curve that looks natural on a wide range of photos. This technique is used by Google Photos card overlays and editorial publication card components.
text-shadow as backup
Even with a well-tuned scrim, a photo with an unusually bright area in the text zone may cause contrast failures. A subtle text-shadow: 0 1px 3px oklch(0 0 0 / 0.5) on the heading text gives an additional 10–15% contrast boost with no visible effect on normal photos. Together, the scrim handles 90% of cases, and the text-shadow handles edge cases where the photo is unpredictably bright. Avoid heavy drop shadows that make text look like it's floating — keep the offset to 1–2px and spread to 3–4px maximum.