 Hi there, my name is Matthias and I work on Chrome DevTools. In Chrome 83 we shipped a new Chrome feature that lets you use DevTools to simulate various vision deficiencies, including color vision deficiencies in the Blink renderer. In this session, I'll explain why we did this, but also how you could implement similar functionality using web technology in HTML, CSS and SVG. And finally I'll explain how we actually implemented this in the Blink renderer in C++. But let's start with the why. What's the most common automatically detectable accessibility issue on the web? Well, it turns out that it's low contrast text. According to web aims accessibility analysis of the top 1 million websites, over 86% of homepages have low contrast. So what is Chrome DevTools doing about that? Well, we already offer several tools that can help developers and designers to improve contrast and to pick more accessible color schemes for their web apps. For example, the DevTools color picker calls out bad contrast ratios for text elements. It shows the recommended contrast line to help manually select better colors and it can even automate that selection by suggesting accessible colors that you can apply with a single click. And this is just one example. We have additional tools like the inspect mode tooltip, the CSS overview tab or the lighthouse accessibility audit. All of these tools mainly focus on surfacing contrast ratio information and giving you options to fix it. We realized that DevTools was still missing a way for developers to get a deeper understanding of this problem space. And this is especially important because roughly one in 20 people suffer from a color vision deficiency. Such impairments make it harder to tell different colors apart, which can amplify contrast issues. To illustrate these various deficiencies, let's look at some photos of crayons. A chromatopsia or monochromacy is the complete inability to perceive any color except for shades of gray. Thankfully, this is an extremely rare color vision deficiency. Due to renopia is the inability to perceive green light, making it difficult to distinguish red from green. Protonopia is the inability to perceive red light and Tritonopia is the inability to perceive blue light. Concretely, what all of this means for web developers is that your web app could have color contrast issues that people with regular vision wouldn't even notice. So in Chrome 83, we shipped a new DevTools feature that lets you simulate vision deficiencies in order to help developers understand these problems and build more accessible experiences. Here's the demo again. I'm using a page that's playing a video to show that this really works with all kinds of web content, even if the page includes iframes, animations, video elements, or anything like that. You can simulate vision deficiencies through DevTools and then click around, scroll, interact with the page in any way you like. Ok, so that's the feature we built. Now let's talk about the implementation. Before we dive into the blink renderer, as a thought exercise, imagine you want to implement this feature using only web technology, so using HTML and CSS. How would you do it? Any ideas? Well, you can think of these emulations as a big overlay that covers the entire page, and the web platform has a way to do that, with CSS filters. With the CSS filter property, you can use some predefined filter functions such as blur, contrast, grayscale, hue rotate, and many more. The CSS filter property also accepts a URL which can point to an SVG filter definition. This gives you even more control over the filter you want to apply. For example, here's a custom filter definition based on a color matrix. Every pixel's red, green, blue, and alpha color value is matrix multiplied to create a new color with modified red, green, blue, and alpha values. Each row contains a multiplier for, from left to right, R, G, B, and A, as well as a fifth column that we'll talk about in a second. The first row of the matrix is used to compute the new red value, the second row green, the third row blue, and the last row alpha. So that's what each row stands for, and there's not four but five columns, because the last one is a constant shift value. We don't need shift in our implementation, so we'll keep those values at zero. Now, you might be wondering, where do these numbers come from? What makes me think this color matrix is a good approximation of Deuteronopia? The answer is science. These values are based on a paper by Machado, Oliveira, and Fernandez from 2009. They developed a color vision deficiency simulation model that is physiologically accurate. We read the paper and used their formulas to create SVG-compatible color matrices. Anyway, so we have this SVG filter, and we can now apply it to arbitrary elements on the page using CSS. And here's what that looks like. This code already gives us the effect that we're after. If we wanted to, we could build our DevTools feature as follows. Whenever the user emulates a vision deficiency through the DevTools UI, we inject the SVG filter into the Inspect document, and then we apply the filter style on the root element. But there are a number of problems with that approach. For example, the page might already have a filter on its root element, which our code might then override. The page might also already have an element with ID Deuteronopia, which would clash with our filter definition. The page could also rely on a certain DOM structure, and by inserting the SVG into the DOM, we might violate these assumptions and break the page. All these edge cases aside, the main problem with this approach is that we would be making programmatically observable changes to the page. If a DevTools user inspects a DOM, they might suddenly see an SVG element that they never added, or a CSS filter property that they never wrote. That would be very confusing. So to implement this functionality in DevTools, we knew we needed a solution that doesn't have these drawbacks. Let's see how we can take our current solution and make it less intrusive. There's two parts to the solution that we need to hide. First, the CSS style with the filter property, and second, the SVG filter definition, which is currently part of the DOM. Let's start with that last one. How can we avoid adding the SVG to the DOM? One idea is to move it to a separate SVG file. We can take this piece of code and save it to an SVG file, but before we do that, we need to make some changes. See, up until now we were using inline SVG in HTML, which follows the HTML parsing rules. That means you can get away with things like omitting quotes around attribute values in some cases. However, SVG in separate files is supposed to be valid XML, and XML parsing is way more strict than HTML. To make this valid standalone SVG and thus valid XML, we need to make two changes. Do you know which ones? The first thing is the XML namespace declaration at the top. The second change is the so-called solidus, the slash that indicates that the filter affects color matrix tag both opens and closes the element. The HTML parser couldn't care less about this slash, but in XML, the difference matters. Anyway, with those changes, we can finally save this as a valid SVG file. Now we can just point to this file from the CSS filter property value. We no longer have to inject SVG into the document. That's already a lot better. But, hmm, now we depend on a separate file. That's still a dependency. Can we somehow get rid of that as well? As it turns out, we don't actually need a separate file at all. We can encode the entire file within a URL by using a data URL. To make this happen, we take the contents of the SVG file that we had before, we add the data prefix, configure the proper MIME type, add a comma, and we've got ourselves a valid data URL that represents the very same SVG document. Except now, we no longer need to store the file anywhere or load it from disk or over the network just to use it. Now, instead of referring to the file name like we did before, we can point to the data URL. At the end of the URL, we still specify the ID of the filter we want to use, just like before. I do want to point out that there is no need to base 64 encode the SVG document in the URL. Doing so would only hurt readability and increase the file size. We added backslashes at the end of each line to ensure that the new line characters in the data URL don't terminate the CSS string literal. So far, we've only talked about how to simulate vision deficiencies using web technology. Interestingly, our final implementation in the Blink renderer is actually quite similar. Here's the C++ helper utility we've added to create a data URL with a given filter definition based on the same technique. And here's how we're using it to create all the filters we need. Note that this technique gives us access to the full power of SVG filters without having to reimplement anything or reinvent any wheels. We're implementing a Blink renderer feature, but we're doing so by leveraging the web platform. Okay, so we figured out how to construct SVG filters and turn them into data URLs that we can use within our CSS filter property value. Can you think of a problem with this technique? It turns out we can't actually rely on the data URL being loaded in all cases because the target page might have a content security policy that blocks data URLs. Our final Blink level implementation takes special care to bypass CSP for these internal data URLs during loading. Edge cases aside, we've made some good progress because we no longer depend on inline SVG being present in the same document. We've effectively reduced our solution to just a single self-contained CSS filter property definition. That's pretty good, but now let's get rid of that too. Because, as we discussed earlier, our CSS filter property might override a filter in the real document and break things. It would also show up when inspecting the computed styles in DevTools, which would be confusing. So, how can we avoid these issues? We need to find a way to add a filter to the document without it being programmatically observable to developers. One idea that came up was to create a new Chrome internal CSS property that behaves like filter but has a different name, like dash dash internal DevTools filter. We could then add special logic to ensure this property never shows up in DevTools or in the computed styles in the DOM. We could even make sure it only works on the one element we need it for, which is the root element. But this wouldn't be ideal. We would be duplicating functionality that already exists with filter and even if we try hard to hide the property, web developers could still find out about it and start using it on the web, which would be bad. We need some other way of applying a CSS style without it being observable in the DOM. Any ideas? I have to admit, at this point I got a little stuck and I couldn't immediately see a good solution. Until one evening I was relaxing at home doing some light reading of the CSS specification as one does. And I came across this section where the CSS spec introduces the visual formatting model it uses. And one of the key concepts there is the viewport. This is the visual view through which users consult the web page. A closely related concept is the initial containing block, which is kind of like a styleable viewport div that only exists at the spec level. The spec refers to the viewport all over the place. For example, you know how the browser shows scrollbars when the content doesn't fit? This is all defined in the CSS spec based on this concept of the viewport. And it turns out this viewport exists within the blink renderer as well as an implementation detail. This is the code that applies the default viewport styles or more accurately the styles for the initial containing block. It handles zindex, display, position, overflow. And there's some other magic related to stacking context which doesn't directly translate to a CSS property. But overall you could think of this viewport object as something that can be styled using CSS from within blink. Just like a DOM element, except it's not part of the DOM. This gives us exactly what we want. We can apply our filter styles to this viewport object which visually affects the rendering without interfering with the observable page styles or the DOM in any way. To recap our little journey here, we started out by building a prototype using web technology instead of C++. And then we worked on moving parts of it to the blink renderer. We first made our prototype more self-contained by inlining data URLs. We then made those internal data URLs CSP friendly by special casing their loading. We made our implementation DOM agnostic and programmatically unobservable by moving styles to the blink internal viewport object. What's unique about this implementation is that our HTML, CSS and SVG prototype ended up influencing the final technical design. We found a way to use the web platform even within the blink renderer. For more information and links to the relevant design docs and patches, check out our detailed article on the DevTools engineering blog. And that's it. If you have any questions or feedback, don't hesitate to ping me on Twitter. Thank you for listening and take care!