 Good boy. Welcome to Designing in the Browser. In this series, we explore how to take a design, build it, and then tweak it in the browser using the latest in web technology and design systems. Today, we're going to take a look at color on the web and how we can use custom properties, also known as CSS variables, to create our own systems of color. This feature gives us a lot of dynamic control to truly allow for design on the fly. What's more, we'll soon be able to extend custom properties, giving them fallbacks and semantic syntax once new technology called CSS Houdini is better implemented across browsers. Let's dive in. You ready to dive in? CSS custom properties are also known as CSS variables colloquially. They allow for dynamics styles that you update on the fly. Custom properties are prefix with a double dash, scope to the element in which they are declared, and cascade just like the rest of CSS. To create a global variable, you can add your custom property to the root of your file in your CSS. To scope this more tightly, however, you can apply the value to any specific elements. This will override that global variable. Variables can be of any value and right now are just parsed as strings of CSS. So it's up to the style sheet author to type check. But in the future with Houdini, we'll be able to give these custom properties a syntax to kind of allow for some error checking. Let's see what custom properties look like with an example, using Google's material design as the base. Material design is an open source UI design system with a set of code components across platforms. The web components are called material design components for the web. So I've opened up the material theme builder tool. This tool helps users create themes using material components for the web. In our last episode, we went over extending CSS and using SAS is one of the ways that we can do that with material design. But you can also use custom properties from some basic theming as well. So here I have the theme builder and I've opened up DevTools where in the HTML, you can see that I have this root with all of these variables defined. I have MDC theme primary, on primary, which is the color on primary, secondary, on secondary error, on error, et cetera. And these colors manifest inside of the components themselves. We have primary theme on buttons, on navigation bars, secondary on the fab button. And I can actually update these. So if I wanted to make this crimson instead of that purple color, if I wanted to make the secondary color a lavender, you can do that. Then I could change the on secondary color to maybe a dark gray. I feel like that might even be too light, so maybe a black. So these are things that you can start doing dynamically in the browser. And we have a lot of great DevTools within Chrome that help us do that. The color palette specifically is one of my favorite Chrome DevTools. Here we have a whole selection palette, which you can dynamically see updating inside the browser. You can adjust the alphas. There's palettes in here. You can really go through all of these color options. And it's a lot of fun to just experiment and see what you can do directly in the medium in which you're delivering, which is the web. We're gonna go over these in a little bit more depth in a future episode, so stay tuned for that. Material also provides a color tool to help you make color choices for your entire application. And this tool is on material.io. So if you open up material.io, go to the design tab and under color, the color system. Within that, there is a section for tools for picking color. So here we have a material palette generator where you can pick your colors just like you could within the Chrome DevDools. But what this allows you to do is then explore complementary colors, analogous colors, things that would fit well together when you're deciding for the color scheme inside of your application. So here I've selected this orange color as the primary. I now am automatically given this option of a 700 complementary blue color, which might pair nicely with that primary color. And then if I click through, you can see there's analogous colors here, triadic. Once I've clicked this, I have it on my clipboard. So it's just a really quick and easy way to sort of start building out your color palettes. Like I mentioned earlier, we can apply these globally or be more specific. Here I'll specify the custom property in the MDC button class. So all the base UI buttons, FAB buttons are included since they get the secondary theme color will be updated. So if I like this orange color, I click it, it's copied to my clipboard. I can go back here, just paste that in there. And now we have that propagated. We also see here inside the drawer, we have a lighter orange behind it. And that's because this is going to be applying opacity on the orange just to highlight those selected colors inside of that drawer. If I wanted to create a color for a specific element and not necessarily the entire application, we can go into any of our components individually and write a custom property that will then just be applied to that element. So for example here, I want to apply a class of the button. Maybe I want to make the MDC theme primary inside that button, a steel blue. There we have just the buttons getting this blue color whereas the rest of the application will get the primary theme color of that orange that I set in the global state. Now you can also change these values with JavaScript. I'll show you a little bit of a silly example where I'm adjusting the background from white to black based on the horizontal mouse position using our GPA just to show you how swift and dynamic custom properties are. I've opened the console and here I'm just going to paste this function which basically looks for the root as the document element which we're applying the styles to, the custom properties to. And here I'm adjusting the MDC theme background color from white to black based on the position of my mouse left to right on the screen. And what I'm basically doing is setting the alpha value on this RGBA color of black from zero to 100 based on the position on the page. So it's just a silly example but it shows you how dynamic these things are allows you to separate your style from your logic because you can apply the styling inside of your CSS. You can apply the logic in JavaScript and you don't have to rewrite it from JavaScript. You still have those separated. You can update your custom properties through your JavaScript. You can even write color functions using CSS custom properties and CSS functions. The calc function allows for this so you can create your own systems using Manil CSS. In order to do this, we're going to be using the HSL color space. HSL stands for Hue Saturation Lightness. We can individually adjust the hue, the color around that color wheel, the saturation which is the amounts of pure color against that 50% gray which would be the distance from the center of that color wheel and the lightness, how much dark versus light is in the color. So how near or far it would be from the light source. So let's take a moment to talk about color theming in general here. We've been having a lot of fun seeing all of these unique themes that people have come up with using the Material Theme Builder and updating a few SAS variables. So here are a few of those examples of these themes. You can go to Material World on Glitch to see them. There are hundreds of examples and it's really been interesting to see how people come up with their own unique aesthetic using a couple of variables. Most website themes have a primary and secondary or highlight color. They'll also have background colors and semantic colors like warnings and things that are saying that it's okay or action items. The primary and secondary will usually have light and dark tints and are used throughout the site. So you have a lot of color variants inside of any application. Keep in mind simplicity is key with color theming. You don't want to have 400 shades of red or gray on a web page. So let's get back to writing these dynamic functions ourselves. HSL is among the easiest ways to do this since we have access to the hue, saturation and lightness individually. So if you're using hex like Material does, we'll want to convert it first. We can grab the primary colors HSL values from our dev tools. A little shortcut to do this with is if you hit Shift in your keyboard and then click on the square, it'll automatically cycle between the hex value, the RGB and the HSL value. You can also always click in the square and these little arrows will also change the color type for you. We'll need to separate these values in order to apply our calculations. So it'll look something like this where we separate them in the primary hue, saturation and lightness values as individual custom properties. Now we can create a variable based on this primary color that will lighten the color itself. This would imitate the SAS light and function. We'll first need to determine what amount of percentage we want to lighten it. And we can set that new variable to something like the lightness transform value. So lightness transform can be something like 10%. Then we'll be able to use that within a new variable. Maybe we'll call that MDC theme primary dash dash light with the double dash to denote a modifier of the original primary color. And inside of that, we can use a calculation. So we'll get the updated lightness value by applying a calculation inside of the custom property. That'll look like this. This primary light value could be a calculation of the original primary light plus the new lightness transform. This will give us 57%. Or anything dynamic if we do change the original custom property values for the primary color theme. Then we can use that inside of our new custom property called MDC theme primary lights. And that'll look like this. We're here with saying the HSL value with the primary colors hue, saturation and new lightness value. So another way to look at this is by applying this HSL value and inside of that lightness, putting the calculation in there, you could break it up. It's really up to you. I'm breaking it up so you can see exactly what's happening in each step. If you wanted to create a darkened function, imitating that functionality in SAS, you can create a darkness transform value. So this darkness would then be a negative color since darkening the color value that we're putting in the lightness of the HSL color. So then inside of there, we could do the same thing where we're applying a transformation. We're adding this darkness transform. And there you go. We have a darkened version of our primary color. You can also do this with complementary and triadic colors. With complementary colors, you can adjust the hue value by adding or subtracting numeric values. If you add 180, you're going from, for example, the top of the color wheel to the opposite side in the color wheel. So that's how you're gonna get the complementary color. You can also do this with triadic colors or really any color in between. And this is how you get the complementary color. From here, we can then set our new theme colors to these new values, or we can use them for our outlines, active state backgrounds, and a variety of places within our UI. You know what's really trendy right now? Dark theme. It's really quick to re-theme a page using custom properties. And here's an example of that using a base material data table. I've created a data table here with little dark theme on-off switch. So in JavaScript, I'm actually just updating the custom property values. If I inspect this, you could see with this dark theme on switch, all of these custom properties are updating. So the primary value, I have a primary light value here, which is updating this color value there. I've got a secondary value, a surface color. All of these are updating via JavaScript. What does that look like? We have a stored variable of root, which is the document elements. And then we've created a switch where we're looking for a checked state and swapping between these two color themes. We have our light theme where we're updating that document to document root, that style, that set property, and we're specifying the property name as well as the value. So because of this, we can now see that we're getting this dark switch and it's just updating the custom property values and allowing for that dark mode to be applied in our code. In this example, the highlight color should probably be a lightened function like I showed you above instead of a static value, but this was just a quick calculation. This could be dynamic if you did take the time to create these color functions and theming with your CSS. And it just shows you how quick and painless it is to really transform your entire application with the flip of a switch. You can also use preferred color scheme for this. Instead of a switch when the user's OS settings are for dark theme, it will show the dark color and we'll talk about that in our responsive color episode a little bit more. So let's talk about CSS Houdini just a little bit in this episode. I love custom properties in CSS so, so much and you can even take them a step further with CSS Houdini. CSS Houdini is a series of merging browser APIs that gives the user a ton of control over the styling of the code that you write. With Houdini, we'll soon be able to apply semantic meaning to CSS custom properties, allowing for fallback values and interpolated interactions that aren't possible today. With Houdini, we'll be able to register properties and provide them with a syntax value, which looks a little bit like this. In this registered property, we're giving it a name, a syntax, which is the definition of what that property is, if it inherits or not, as well as an initial value, which is a fallback value in case something goes wrong and it allows for error checking when you are updating that value or just having a fallback default value for your custom property. So what does that look like? Let's open an example. Here we have a example of a box. I'll show this next to the code here. And it's a standard box with a color stop that we're animating a linear gradient. We're going from left to right and we're then updating the color stop to yellow and transitioning all. But when I hover over this, you'll see that there's not really any transition. It's not actually transitioning. And that's because there's no meaning to this custom property. It's a string-based custom property. And right now in CSS, there's no way to make a gradient smoothly animate. And that's where Houdini comes in. With Houdini, we can now do this. So here's another example where we're using a custom property that I've created here in props.js. I'm giving it a name called color primary, initial value of hot pink, and a syntax of color. I'm also checking to see if custom properties are supported in this browser, which is really important because it is still an emerging technology. In the CSS, pretty much this looks the same. I'm updating a variable called color primary now to yellow from that hot pink color. And I have this transition of 500 milliseconds on that property value. So if I look at this next to the code and hover over this, you now see a nice transition fade from that pink to that yellow. So that was just a little touch on Houdini, but custom properties in CSS are just super cool and allow us to have so much control over our styles as well as separate our code's programmatic logic like the JavaScript from the styling, the visual logic in the CSS. However, keep in mind browser compatibility. Custom properties work in all modern browsers, but if you have to support older browsers and make a more robust API for your design systems, you may want to use SAS variables and color functions for that. I hope that you had fun with us today on this color exploration journey and we'll see you next time. Bye, say bye disco.