CSS: working with text in images

You may come across a UI component that has text above the image. In some cases, depending on the image used, the text will be difficult to read. There are several different solutions to this problem, such as adding a gradient or darkening the image, applying a drop shadow to the text, and others. This technique will be useful not only frontend and web developersbut also ui / ux designers. I was inspired to write this article by a tweet from Addy Osmani working on Google Chrome.

Here, I’ll cover various approaches and solutions to this problem, and how to expose the user interface to the front-end developer in a way that ensures that the UI is implemented according to the design layout, as some details in CSS can easily be overlooked.


How Spotify makes text in images more readable: Linear Gradient CSS Overlay. A more common but still effective method of improving color contrast these days

Introduction

Let’s take a look at our task. When designing a component with text over an image, we must make sure that the text is easy to read.

Left without overlay, right with overlay.

Note that the non-gradient version is not readable well. This is not good for the user. To solve this problem, we need to add a layer below the text so that the text is easy to read. Adding this layer can be a daunting task, and I’ve seen many implement this solution without considering accessibility.

Possible solutions overview

Let’s take a look at the possible solutions.

There are solutions that require more attention – these are gradient solutions. Why? Because adding a gradient layer is very easy, but the text won’t be handy.

Solutions

Gradient overlay

Generally speaking, overlaying a gradient is the most common solution for making text in an image stand out more clearly. With this in mind, I will dwell on it in a little more detail.

When implementing a gradient overlay, we have two options:

  • Use a separate element for the gradient (pseudo element or empty <div>)

  • Apply gradient as background image.

Each of the above has its pros and cons, let’s take a look at them.

The content element is absolutely positioned with a gradient as its background image. This means that the size of the gradient is equal to the height of the element.

.card__content {
  position: absolute;
  /* other styles (left, top, right, and padding) */
  background: linear-gradient(to top, rgba(0, 0, 0, 0.85), transparent);
}

At first glance, it might seem like a gradient works well. This is not true. I tested the same gradient with a lot of images and here is the result.

Note that the contrast between white text and images is not always clear. Some people will read the text, but using a gradient like this is a huge mistake.

The reason is that the gradient needs to cover more vertical space, so it needs to be taller. If the gradient is equal to the size of the content, it will not work in all cases. To solve this problem, we can use min-height as shown below:

min-height to the .card__content element.

Flexbox to move content down.

.card__content {
  position: absolute;
  /* other styles (left, top, right, and padding) */
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.85), transparent);
}

Another solution is a large padding-top, with it min-height and flexbox are not needed.

.card__content {
  position: absolute;
  padding-top: 60px;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.85), transparent);
}

Note the difference between the left and right cards. Gradient more in height.

Looks nice. Can we do better? Definitely yes!

Softening the gradient

Looking closely, you will notice where the gradient ends, that is, it has a sharp border.

To do better, we can apply softening the gradient… This will make the gradient look more natural and you won’t notice any hard edges at the end.

To achieve smoothness in CSS, we need multiple gradient transition borders, but at the time of this writing, there is no native way to render multiple borders in CSS. The good news is that the CSS Working Group discusses the ability to implement native gradient softening in CSS, but it remains unclear when this feature will be implemented.

Fortunately, Mr. Andreas Larsen has created handy PostCSS and Sketch plugins that help transform a harsh gradient into a softer one.

Here’s the CSS gradient for the example above:

.card__content {
  background-image: linear-gradient(
    0deg,
    hsla(0, 0%, 35.29%, 0) 0%,
    hsla(0, 0%, 34.53%, 0.034375) 16.36%,
    hsla(0, 0%, 32.42%, 0.125) 33.34%,
    hsla(0, 0%, 29.18%, 0.253125) 50.1%,
    hsla(0, 0%, 24.96%, 0.4) 65.75%,
    hsla(0, 0%, 19.85%, 0.546875) 79.43%,
    hsla(0, 0%, 13.95%, 0.675) 90.28%,
    hsla(0, 0%, 7.32%, 0.765625) 97.43%,
    hsla(0, 0%, 0%, 0.8) 100%
  );
}

Compare cards with and without gradient softening.

Horizontal gradients

Working with text on top of an image cannot touch only the vertical gradient, you can work with a horizontal one as well.

Here is the CSS for the gradient for the section above. To soften the gradient, I used the tool mentioned earlier.

background: linear-gradient(
  to right,
  hsl(0, 0%, 0%) 0%,
  hsla(0, 0%, 0%, 0.964) 7.4%,
  hsla(0, 0%, 0%, 0.918) 15.3%,
  hsla(0, 0%, 0%, 0.862) 23.4%,
  hsla(0, 0%, 0%, 0.799) 31.6%,
  hsla(0, 0%, 0%, 0.73) 39.9%,
  hsla(0, 0%, 0%, 0.655) 48.2%,
  hsla(0, 0%, 0%, 0.577) 56.2%,
  hsla(0, 0%, 0%, 0.497) 64%,
  hsla(0, 0%, 0%, 0.417) 71.3%,
  hsla(0, 0%, 0%, 0.337) 78.1%,
  hsla(0, 0%, 0%, 0.259) 84.2%,
  hsla(0, 0%, 0%, 0.186) 89.6%,
  hsla(0, 0%, 0%, 0.117) 94.1%,
  hsla(0, 0%, 0%, 0.054) 97.6%,
  hsla(0, 0%, 0%, 0) 100%
);

Blending solid color and gradient

I found out about this trick on the Netflix website. The home page for an unregistered user has a header with a large background image.

I like it, but it hides a lot of the image details. Use this technique only if the image needs to be decorative (does not bring real benefits to the end user).

<div class="hero">
  <img src="https://habr.com/ru/company/skillfactory/blog/551850/cover.jpg" alt="" />
  <div class="hero__content">
    <h2>Unlimited movies, TV shows, and more.</h2>
    <p>Watch anywhere. Cancel anytime.</p>
  </div>
</div>
.hero:after {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.4);
  background-image: linear-gradient(
    to top,
    rgba(0, 0, 0, 0.8),
    rgba(0, 0, 0, 0) 60%,
    rgba(0, 0, 0, 0.8) 100%
  );
}

Here is a visual explanation of how this pattern works.

Gradient overlay and text shadow

There is a nice little detail that can make text over images even better. It’s all about adding a light shadow to the text. Even when not easy to spot, it can be very useful if the image is not loading. Let’s look at an example.

.whatever-text {
  text-shadow: 0 2px 3px rgba(0, 0, 0, 0.3);
}

Gradient overlay, text shadow and opacity

This is a pattern I noticed in the Facebook video player. I liked that they used several techniques to make the text and other UI elements legible. When working with the turntable, it is very important to ensure that the elements above it are visible.

.player__icon {
  opacity: 0.9;
}

.player__time {
  color: #fff;
  text-shadow: 0 0 5px #fff;
}

What’s new in this? Icons and the player have 90% opacity. This helps them blend in with the background below them. It feels like the controls are intertwined with the image.

Also, white shadow on white text is an effective way to sharpen text. Do you want proof that all of the above will work even if the background is completely white? Here you go.

Text and interface elements are easy to read, even when the image is completely combat

Youtube does the same with its videos.

Here’s what I liked about Youtube’s approach:

  • Dark border for each icon to make it stand out better.

  • Black shadow instead of white for video time.

Radial gradient

An interesting solution I learned about from Netflix is ​​radial gradient. This is how it works:

  1. Set the foreground color of the background.

  2. Place the image in the upper right corner with a width of 75%.

  3. The overlay matches the size and position of the image.

.hero {
  background-color: #000;
  min-height: 300px;
}

.hero__image {
  position: absolute;
  right: 0;
  top: 0;
  width: 75%;
  height: 100%;
  object-fit: cover;
}

.hero:after {
  content: "";
  position: absolute;
  right: 0;
  top: 0;
  width: 75%;
  height: 100%;
  background: radial-gradient(
    ellipse 100% 100% at right center,
    transparent 80%,
    #000
  );
}

However, the Netflix team used a .png image as an overlay. I do not know the exact reason for this. Maybe it’s a cross-browser issue or something like that: I haven’t tested the radial gradient solution.

Choosing a user-friendly overlay color

I’ll show you a great tool to help you choose the correct overlay opacity depending on the image. Take a look See it on Codepen. An interesting challenge is to make the gradient usable.

In general, if you can ensure that the gradient overlay fills the text correctly and has an appropriate color contrast, you’re good to go.

Testing

A solution isn’t good until it’s tested, right? One way I use to test the gradient overlay is adding a white background below the gradient. If the text is readable after that, then the gradient will work with any image, and if not, you need to tweak and enhance it.

In the example above, I chose a solid color under the heading, and the contrast ratio is 4.74, which is considered good.

Working with Firefox DevTools

Thanks to Gijs Weijfeiken: he told me that Firefox can test color contrast on gradients. This is a great feature.

If you want to pump yourself into web development, master the specialty frontend developer or become real Fullstack-all-rounder – come and learn, and our experienced mentors and attentive curators will help you reach the final.

find outhow to upgrade in other specialties or master them from scratch:

Other professions and courses

Similar Posts

Leave a Reply Cancel reply