Ronalds Vilciņš

CSS Image Inversion With invert() and SVG Filters

To invert an image with CSS, apply filter: invert(1) to the image itself. Black becomes white and white becomes black, while the other colour channels are inverted too.

CSS
.image-inverted {
  filter: invert(1);
}

That makes CSS image inversion useful for a deliberately reversed image or a monochrome asset. For an icon that should simply follow the text colour, an inline SVG is usually easier to control.

CSS image inversion starts with the image element

Use a class on the image rather than the surrounding article or card:

HTML
<img class="image-inverted" src="diagram.svg"
     width="240" height="120" alt="Connection diagram">

The example needs your own diagram.svg. Keep the dimensions appropriate to that asset and describe what the diagram communicates in its alternative text.

MDN’s invert() reference documents both numeric and percentage values. invert(1) and invert(100%) describe complete inversion; invert(0) leaves the colours unchanged.

Transparent parts of a source image do not become an opaque background just because the colours are inverted. Put the asset on its intended background when judging the result.

For a photograph, inversion changes the whole colour treatment. It will not selectively turn only a dark logo white while preserving everything else around it.

Compare the result before choosing a percentage

A small comparison is more useful than guessing at values:

CSS
.original { filter: none; }
.half { filter: invert(0.5); }
.full { filter: invert(1); }

Apply the classes to three copies of the same image. At 0.5, colour channels meet at neutral grey, rather than producing a subtler version of the fully inverted picture.

This is why turning the effect down can remove detail unexpectedly. Use partial inversion as an effect you have inspected, not as a general fix for contrast.

The separate article on inverting element colours covers applying the effect to a whole element or page. Keeping the filter on the asset avoids changing unrelated text and controls.

Keep combined filters in one declaration

CSS filter functions run in the order written, according to the filter property documentation. Order can change the result:

CSS
.invert-then-dim {
  filter: invert(1) brightness(0.5);
}

.dim-then-invert {
  filter: brightness(0.5) invert(1);
}

A black pixel becomes white and then grey in the first rule. In the second, it remains black during dimming and becomes white during inversion.

Put both functions in the same filter declaration. A later rule setting filter: brightness(0.5) replaces the earlier filter value instead of appending another effect.

Other combinations remain useful for deliberate treatments:

CSS
.image-treatment {
  filter: invert(100%) grayscale(50%) sepia(25%);
}

.muted-image {
  filter: grayscale(30%) blur(3px) contrast(150%);
}

The second rule is an example of a different treatment, not a required companion to inversion. Keep only the functions that contribute to the effect you want.

Let inline SVG icons follow the text colour

For a simple icon, SVG currentColor avoids transforming an existing palette:

HTML
<a href="/downloads/">
  <svg width="16" height="16" viewBox="0 0 16 16"
       aria-hidden="true" focusable="false">
    <path fill="currentColor" d="M7 1h2v8l3-3 1 1-5 5-5-5 1-1 3 3zM2 14h12v2H2z" />
  </svg>
  Downloads
</a>

The inline path uses the link’s text colour. The visible link text supplies the accessible name, so the decorative SVG is hidden from assistive technology.

This approach is for inline SVG. An SVG loaded through an <img> is a separate image and does not receive the surrounding page’s text colour in the same way.

I would choose the inline version for an interface icon that needs to match a link. Inversion makes more sense when reversing the source image is itself the desired result.

Use an SVG filter when channel maths is the requirement

A custom feColorMatrix can express a full inversion while keeping alpha unchanged:

HTML
<svg width="0" height="0" aria-hidden="true">
  <defs>
    <filter id="invert-channels" color-interpolation-filters="sRGB">
      <feColorMatrix type="matrix" values="
        -1  0  0  0  1
         0 -1  0  0  1
         0  0 -1  0  1
         0  0  0  1  0" />
    </filter>
  </defs>
</svg>

Reference the filter from CSS:

CSS
.custom-inverted {
  filter: url(#invert-channels);
}

For full inversion alone, that is much more markup than invert(1). The matrix earns its place when the actual transformation needs independently controlled channels.

Asset-specific rendering problems still deserve an isolated example. The original Safari SVG discussion and colour-inversion experiment are useful debugging context, not a reason to apply an undocumented workaround to every image.

Start with the actual asset on its actual background. If invert(1) produces the intended result, keep the one declaration.

Filed under on

Last updated on

← Back to all articles