Dynamic CSS with Custom Properties (aka CSS Variables) DRY Fallback Strategies
Transcript from the "DRY Fallback Strategies" Lesson
>> So this is a very simplified button which has two variations. With this demo I want show you how custom properties allow us to create multiple variations of components really easily and probably the most basic component ever is a button. So here we have this flat button with a color variation.
[00:00:28] The HTML looks like this. And here we have the CSS that sets the actual colors. I've omitted the CSS that sets padding and margins and stuff because that's not that relevant. So look at how this works, for the variation we have, we need to repeat the actual color multiple times.
[00:00:48] So, how could we simplify this with custom properties? You could probably guess at this point we would be setting a color variable with the actual color and using it, And we will use it here as well and here. And this means that our variation can just set a color variable and we can omit everything else, it just works the same.
[00:01:17] Notice however that I did what I said you should not do. I included the value for my custom property in my base rule, which means that everybody that wants to style my component now needs to override the specificity of this rule. This is not too bad in this specific case because the selector is just a button.
[00:01:39] But in a real website, this would not just be a button element, it would probably have classes and all sorts of things. So overriding it's specificity might end up being quite involved. So in general, we should avoid this practice and we should not be setting values here. So what can we do?
[00:02:02] We can use the fullback parameter and specify a fullback here. And here. And I missed the closing parenthesis. Well, that broke really badly was just the closing parenthesis. And now this works the same, but there is a bit of a problem. We've repeated this fall back multiple times.
[00:02:28] So that's not ideal. We don't like repetition. Repetition means that every time you need to make a change to your CSS, you have to change it multiple times. It's much more likely to get out of sync. It means you could end up with broken code later down the line.
[00:02:44] There is this rule in software engineering called DRY which stands for don't repeat yourself. Basically, you should avoid repetition as much as possible. There's a new revision of this rule that says it's okay to write some things twice. But in general, try to avoid repetition. And I mean this is what variables are great for, avoiding repetition.
[00:03:05] So what can we do? We can set the initial value. On a variable. And this is fine to define in the actual rule because we don't actually expect people styling our components to change the initial value. So we can do this. And everywhere where we have black. We can do that.
[00:03:31] And this works, but it's kind of verbose, isn't it? So what else can we do? So instead of specifying a variable for the initial value, we can actually have a separate, hidden variable of sorts for our actual color that we use in our component. And that variable will be set to whatever the color variable is.
[00:04:08] Whoops. Plus a fall back. So that variable will always have a value. And therefore we can use it without a fallback So our code can just use this underscore color variable instead of this huge long string. And as you can see, this works just fine. And of course, as we've seen you could always do this.
[00:04:52] You could register your color property. You could register it as a color, we do want it to inherit and the initial value would be black. And then we can just use the color variable. No need for any fake private variable, it works just fine. But as we've seen the problem with that is browser support.
[00:05:16] There is also another problem that you might want to specify different fallbacks for the same property per rule, and once you register it, that's it. It's registered for your entire CSS. It's registered globally. It's registered with the same initial value everywhere. In some cases, you might not want that.
[00:05:37] But in most cases, this is the superior solution. And it's just that browser support doesn't allow us to use it yet. So let's go back to the previous one. Oops here we want this and here. All right and this works fine. So to recap there are three fallback strategies for dry fallbacks.
[00:06:04] The first one is to have the fallback in a variable. This is very verbose, but in some cases it might make sense. It does have the benefit that if someone really wants to override the default itself, they can do it. The second one is to use pseudo private properties.
[00:06:51] So the second solution is to use pseudo private properties that already have a fallback so they always have a value and we can use them without a fallback. And the third one is to use that property, which is in most cases ideal but not supported everywhere yet. So, another huge benefit of using custom properties is encapsulation.
[00:07:17] People styling our component can style it with the custom property without actually knowing how we've structured our CSS. Without knowing how we've achieved the values effects we've achieved. They only know what we have exposed through this styling API of sorts. Let's look at an example. So here you see that when we hover over these this flat button, the color changes abruptly, there is no transition, there is no effect.
[00:07:49] So what if we wanted to instead have a transition where the border just grows inwards until it fills out entire button. How can we do that with a background you ask? We can't, we would need to use a different technique to draw the background which is not an actual background.
[00:08:08] What we would have to do is use a box-shadow instead with no offsets, no blur and just. A large spread and it would be the same as this color and it would begin set cuz we want it to be drawn inside the element. And we would not have a background.
[00:08:31] Notice that now it works the same, even though it's actually now a book shadow. It looks identical, but we can actually animate the book shadow. See that. So what's the takeaway here? Even though we've used this horrible hack to style a component the way we wanted, nobody using our component and styling our component needs to know about it.
[00:09:05] If in the future, we get some crazy CSS way to be able to animate backgrounds that way, we can just get rid of this hack. We can use a regular background and nobody needs to change their code giving styling our component with different colors. So I think this is probably one of the most important takeaways.
[00:09:24] That CSS variables enable theming that is independent of CSS structure. And this doesn't necessarily just apply to components. A good practice is to structure your CSS in a modular way so that different parts of your CSS can be independent. And this means that these different parts don't need to know about all the other parts.
>> What happens if you define the property syntax as color but you set a number or something else?
[00:10:12] Because then you get actual feedback, you get actual errors that help you debug whereas with the property rule you just sit there and look at your code and wonder what did you mess up. And another thing also for these initial values, is they cannot depend on variables. They need to be independent.
[00:10:30] They cannot be relative values. They cannot depend on other variables. The initial values you specify need to be, the browser needs to be able to resolve them without looking at other elements basically. Just like the built in initial values, just like the initial values that other properties have.
>> Is it practical to store gradient background color stops with variables along with the gradient direction?
>> Yeah, in fact that actually makes it much easier to animate the gradients. As we'll see in the transitions in animation section. This is like a primary use case for registering custom properties, being able to store like parts of gradients so that you can animate them individually.