Skip to content
AgileLogics
6 min read

Images, Filters, and Blend Modes

Prerequisites: Backgrounds, gradients, and shadows, Sizing elements

object-fit and object-position

When an image is given a size with a different aspect ratio, object-fit decides how it fills the box:

.thumb {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  object-position: center 30%;
}

.logo {
  width: 10rem;
  height: 4rem;
  object-fit: contain;
}
Value Result
fill (default) Stretch to the box — distorts
cover Fill the box, cropping the overflow
contain Fit entirely inside, leaving empty space
none Natural size, cropped
scale-down The smaller of none and contain

Works on img, video, and other replaced elements.

Filters

.muted { filter: grayscale(100%); }
.muted:hover { filter: none; }
.soft { filter: blur(2px) brightness(1.1); }
.icon-shadow { filter: drop-shadow(0 2px 4px rgb(0 0 0 / 30%)); }

.glass {
  background: rgb(255 255 255 / 60%);
  backdrop-filter: blur(12px);
}
  • filter functions: blur(), brightness(), contrast(), grayscale(), sepia(), saturate(), hue-rotate(), invert(), opacity(), drop-shadow().
  • backdrop-filter blurs what's behind a translucent element — the "frosted glass" effect. Keep text contrast in mind.

Blend modes and masks

.duotone { background: url("/img/team.jpg") center / cover, teal; background-blend-mode: multiply; }
.headline-over-image { mix-blend-mode: difference; color: white; }
.fade-bottom { mask-image: linear-gradient(to bottom, black 70%, transparent); }
.avatar { clip-path: circle(50%); }
.ribbon { clip-path: polygon(0 0, 100% 0, 90% 50%, 100% 100%, 0 100%); }
  • background-blend-mode blends an element's background layers; mix-blend-mode blends the element with what's under it.
  • mask-image hides parts of an element using the alpha channel of an image or gradient.
  • clip-path cuts the element to a shape.

image-set()

Choose background images by resolution or format:

.hero {
  background-image: image-set(
    url("/img/hero.avif") type("image/avif"),
    url("/img/hero.jpg") type("image/jpeg")
  );
}

Checking the result

<style>
  img { width: 200px; height: 200px; object-fit: cover; filter: grayscale(1) contrast(1.2); }
</style>
<img src="photo.jpg" alt="Example" width="400" height="300">
<script>
  const s = getComputedStyle(document.querySelector("img"));
  console.log(s.objectFit, s.width, s.height, s.filter);
</script>
cover 200px 200px grayscale(1) contrast(1.2)

CSS width and height override the HTML attributes for display, but keeping the attributes still gives the browser the aspect ratio early.