Hero Media Grading: Three Overlay Layers Guarding Text Contrast
A white headline nearly vanished over a bright hero photo. One flat dim layer was not enough; three overlay layers plus text shadows fixed it.
TL;DR
Text disappears on bright images. A layered approach solves this. Contrast is improved with dimming, scrim, and glow.
The main image on the front page of a government portal displays a scene with a clear sky. When the slider switches to this image, the white headline at the bottom almost disappears: the text is still there, but it is overshadowed by the brightness of the sky. In the next image, which is darker, the same text is easily readable. The problem is not the font size, but the contrast between the text and the background.
Just Dimming Is Not Enough
The first solution that comes to mind is to add a transparent black overlay on top of the entire image. A black overlay with 35 percent opacity does indeed calm down the brightest images. However, two weaknesses emerge.
First, the image becomes uniformly dark. The parts that do not contain text are also covered, and the details that make the image attractive are lost. Second, on busy images, such as a row of buildings or a crowd of people, a uniform 35 percent dimming is still not enough to counteract the brightness right behind the text. The need for contrast is not uniform: the text is concentrated on one side of the slide, while the overlay is divided evenly across the entire surface.
Three Layers Working Together
The slider component eventually uses three layers stacked on top of each other, and all of them are made non-interactive. The first layer is a flat 35 percent black dimming as the basic setting. The second layer is a directional scrim: a CSS linear gradient of 90 degrees that graduates from the caption side. Its value is rgba(4,21,15,0.94) at the left edge, decreasing to 0.72 at 30 percent of the width, 0.28 at 55 percent, and then transparent before reaching 74 percent. The text stands in the darkest zone; the direction to the right gradually restores the original image [4].
The third layer is a radial glow: a 520-pixel circle with a core of rgba(5,150,105,0.38) and a blur of 6 pixels, placed behind the caption. Its function is not to darken, but to lift the caption area with a soft green emerald light without a hard edge. Meanwhile, the base color of the caption gradient is changed from generic black to #04150f, a deeper black-emerald, so that the entire grade feels intentional on a single color palette.
.dim {
background-color: rgba(0, 0, 0, 0.35);
pointer-events: none;
}
.scrim {
background-image: linear-gradient(90deg, rgba(4, 21, 15, 0.94) 0%, rgba(4, 21, 15, 0.72) 30%, rgba(4, 21, 15, 0.28) 55%, transparent 74%);
pointer-events: none;
}
.glow {
background-image: radial-gradient(520px circle at 35% 65%, rgba(5, 150, 105, 0.38), rgba(5, 150, 105, 0.12) 45%, transparent 70%);
filter: blur(6px);
pointer-events: none;
}
Layered Shadows and Two Levels of WCAG Contrast
The overlay darkens the background; text-shadow keeps the text readable on images that still dominate in some areas. This property accepts a list of shadows separated by commas, each consisting of horizontal offset, vertical offset, blur radius, and color; the first shadow in the list is drawn on top [2]. The recipe has three ingredients. An 11-pixel kicker uses one shadow of 0 1px 6px. Headlines that appear 24 to 36 pixels use two shadows at once: 0 2px 14px as a wide halo and 0 1px 3px as a sharp edge. Subtitles use 0 1px 8px in the middle.
This division follows the two levels of contrast in WCAG 1.4.3: normal text requires a minimum contrast ratio of 4.5:1, while large text requires 3:1 [1]. Large headlines are included in the second category because their font is thick and wide, so a wide halo is sufficient. An 11-pixel kicker is not. Small letters require a full ratio, and that is why they get their own shadow even though they stand on the same scrim as the headlines.
One note on interaction: each overlay layer uses pointer-events: none [3]. The MDN documentation notes a small exception, namely that pointerenter and pointerleave events are still triggered on the parent element. For the slider, this behavior is sufficient; what is prevented is only that these layers swallow clicks intended for the call-to-action buttons on the slide.
The final grade is evaluated visually on several types of images: bright skies, dark buildings, and crowds. What is taken away is not a magic number, but a division of labor. Dim sets the basic lighting, scrim maintains the text zone, glow smooths the transition, and text shadows cover the remaining gaps. Each layer has a contrast reason that can be explained, not just a matter of taste.