Skip to main content

Using SVG Filters to Apply an Accessible, High-Contrast Filter to Content

Mar 06 '18

Designing and developing a website with accessibility compliance can be a very tricky task. Not only do the designer and developer have to keep in mind that their creation will need to be usable on mobile and desktop, it also needs to be usable by blind and low-vision users via a screen reader or a similar application. For low-vision users specifically, the website design needs to have enough contrast between the text and what's behind it to be readable. Many websites add a toggle button for these users somewhere in the header or footer that can turn on the high-contrast version.

SVG filters make it possible to reuse existing image assets and still be able to obtain that high-contrast design.

Let's assume that you have a button in your header or footer for "Toggle High-Contrast.” Clicking this button will add a CSS class to the body "high-contrast." Then, somewhere on your page, you have a callout of some kind:

<div class="callout">
  <img alt="Yellow Badge" src="" />


With associated CSS:

.callout {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 280px;

.callout div {
  z-index: 1;
  color: #fff;
  font-size: 2em;

.callout img {
  position: absolute;



It would look something like this:

That would be completely readable for a normal user , but for some low-vision users, the white text on the yellow background would completely disappear.

One possible solution is to force the yellow background image to be darker. Somewhere in your page, create an SVG element that has a definition for your high contrast filter:

<svg version="1.1" xmlns="" xmlns:xlink="" style="display:none">
    <filter id="high-contrast-filter">
      <feColorMatrix type="saturate" values="0" result="desat"/>
        <feFuncR type="discrete" tableValues="0 0 0 0 0 0 0 0 0 0 0 0 0 0 .8 .9 1" />
        <feFuncG type="discrete" tableValues="0 0 0 0 0 0 0 0 0 0 0 0 0 0 .8 .9 1" />
        <feFuncB type="discrete" tableValues="0 0 0 0 0 0 0 0 0 0 0 0 0 0 .8 .9 1" />



The ID of the filter can be anything you want it to be, but remember what you name it because you'll be using it soon. The first line of the filter, `feColorMatrix`,  converts the element to grayscale. The second part, `feComponentTransfer`, applies a posterize-type effect to the element by breaking down each RGB color channel into a discrete list of values between 0 and 1 in `tableValues`. Because the image is now grayscale (thanks to `feColorMatrix`), each RGB color channel is processed in the same way.

Next, add a section in your CSS that applies the SVG filter to the yellow background image when the site is in high-contrast mode:

.high-contrast .callout img {



The ID here is the one you defined earlier. It should look something like this:

Now, even low-vision users can read your callout, and you didn't even have to create any additional images to achieve it!

Some notes and caveats:

  1.  This filter is good for converting most colors to black, but some colors (especially lighter colors) will instead get converted to white. You can play with the `tableValues` under `feComponentTransfer` to figure out what works best for you. You might even need to define multiple filters that work best on different types of images.

  2.  Applying this filter to elements with text will remove any font anti-aliasing that might be applied by the browser and/or OS. It is not a huge difference, but it can be noticeable.

  3.  SVG filters must be applied to elements directly, and will affect any children elements under that element and their backgrounds. This means you can't apply the filter to the background of an element without also affecting the contents.

Further reading and tools:




Read more about: