Submind YouTube summaries
Thumbnail for 92: CSS if() and custom functions

92: CSS if() and custom functions

Watch on YouTube

Video summary

In this episode of the CSS podcast, hosts Yuna and Brahmise welcome back after a hiatus, marking a significant milestone as they celebrate five years since the show's inception. They announce that Brahmise is officially joining as a permanent co-host following his previous guest appearances focused on scroll-driven animations and view transitions. The duo introduces themselves to new listeners, highlighting their extensive backgrounds in UI engineering, design systems, and contributions to major CSS features like container queries and form controls. With the team refreshed, they dive into two powerful new capabilities within the CSS Values and Units Level 5 specification: inline conditionals using the `if()` function and the introduction of custom functions, both of which are now available in Chrome version 137. The first major topic discussed is the `if()` function, which allows developers to write conditional logic directly inside property values without needing separate media queries or complex hacks like the space toggle method previously used for length-based conditions. The hosts explain that while CSS has always supported conditionals through pseudo-classes and style queries, the new inline syntax lets you query the element itself using `supports`, `media`, or `style` functions within a single declaration. They demonstrate how to structure these statements with multiple lines for better readability, showing examples where background colors change based on viewport width or custom property values. This feature significantly simplifies code by allowing developers to handle responsive behaviors and state changes in one concise line rather than repeating selectors across different media queries. The second half of the episode focuses on custom functions, which act as complex, reusable logic blocks that accept arguments and return single output values. Unlike standard custom properties that only store static data, these functions can contain internal CSS rules, including media queries and calculations, to dynamically determine their output based on input parameters. The hosts walk through examples such as a `negate` function for flipping length values and a more advanced `conditional-radius` function that automatically removes border radii when elements approach the viewport edge. They emphasize that function arguments behave like custom properties with optional types and default values, and they introduce the special `result` descriptor used to define the final value returned by the function body. Throughout the discussion, the hosts highlight how these new tools enable cleaner, more maintainable CSS by hiding complex logic behind simple function names, effectively creating a utility-first approach similar to JavaScript libraries but within pure CSS. They note that while the syntax might initially seem dense, seeing it in practice makes the logic straightforward and readable. The episode concludes with excitement about the future of CSS development, where shared function libraries could emerge to standardize common patterns like reset styles or layout utilities. Both hosts encourage listeners to experiment with these features using resources linked in the show notes and invite the community to share their own creative implementations as the language continues to evolve.
Read the full video transcript
[music] Welcome back to the CSS podcast where we talk about the latest and greatest in the CSS and UI world. And UI is the most exciting space in web development right now. So, we have a lot to talk about. In case you're new to the show, my name is Yuna >> and my name is Bis, >> your guides, your co-hosts, and your CSS BFFs. It's been a while. It's been a journey. A lot has happened, but we're happy to finally be back. And I have some good news and unfortunately some bad news. The bad news is that Adam is no longer a part of the show. We all miss having him on the team. >> Yeah, miss you, buddy. >> But I'm happy that we get to continue the legacy that Adam and I both started together and kept going for 5 years. Can you believe it's been five years since we started this thing? >> Five? Wow. >> Yeah, it's year five. The good news is that Brahmise is joining me permanently as a co-host of the CSS podcast. >> Hello. Last year, Brahmis joined us to talk about scroll driven animation and view transition as we rounded out the season, and he has a wealth of CSS knowledge, is super involved in the evolution of this language, and has a lot to talk about. So, I'm really excited to officially welcome Braise to the show crew, not just as a guest, but as a co-host this time. And I know that I have a lot to learn from you and our audience has so much to learn from you, too. So, I'm excited to kick off this season with a topic that I think we're both really excited about. But before we do, for those that don't know us or are just joining our podcast, hello. I think that we should do a little introduction. Brahmise, as you're new to the show, do you want to go first? >> Yeah, sure. So, uh, my name is Bruce. I'm from Belgium and for the past three and a half years, I've been part of the Chrome Devil team. I've been covering mostly animation related things such as the linear easing function, scroll driven animations and view transitions, but you might also have seen my name float binding articles on cascade layers, add scope, accordions, viewports and so on. Um, I'm also a member of the CSS working group and in my spare time I like to go scuba diving. >> Okay, but Belgium and scuba diving sounds very cold. I don't know how you do it. [laughter] >> It it it is very cold. Um, my coldest dive was in the water of 3° C, which is 37 Fahrenheit for your US people out there. Or if you are a Canadian oven that is also in Fahrenheit, and I don't recommend doing that. It was so cold. So cold. >> That sounds like a polar plunge, not like a scuba trip. So, hi everyone. I'm Yuna, the other half of the CSS and UI Devril team. Uh, this space is definitely a passion of mine. I have a history of working in UI engineering and design systems. And since joining Google six years ago, I've been a part of many CSS feature launches. The most notable of which is probably container queries and a bunch of other responsive design features and all of the recent form controls work including popover, anchor positioning, and customizable select. I was also a part of the interop initiative when it began and an early member of open UI. And I also organized a lot of the initiatives that our team puts out like our annual wrapped series and this very podcast that you're listening to. But I'm probably most well known for my oneline layouts which kind of went viral in 2020. >> Oh, and and don't also forget that you yourself went viral at Google IO in 2024, but that's maybe a different story. >> Those who know. [laughter] >> So, um, in this first episode of the new season, we're going to dig into two very new powerful CSS features. Um, the first one is inline conditionals with the if function and the second one is custom functions. So let's start with the if function. This one gives you a way to write conditional logic right inside of a properties value. It's part of the CSS values 5 specification and has been available in Chrome since Chrome 137. To be clear, CSS has always had conditionals built right into it. Pseudo classes like the hover selector, for example, are technically conditionals and these were built into the language from the start. or the more recent style queries using the at container rule are also a way to write conditionals. But with style queries, you need a parent element to query, whereas the inline if statement now lets you query the element itself. And that's a really nice capability. >> Yeah. And that's what makes it really exciting. Now, there are some hacks to achieve this before, like the space toggle hack before, but those hacks were often convoluted or were limited to only lengths. with inline if this is now much easier to write and it works with any property. >> Wait, so what is the space toggle hack? >> Oh, it's it's a very convoluted hack that uses a custom property that you set either to a single space or the value of initial. And when you use that in a var function, it will either return that space or the fallback in case the value was initial. So you you basically had a way to flip things on or off and do conditionals that way. Um there there's a good article on CSS tricks that summarize it nicely for you. You can find the link in the show notes. >> Okay, I'll definitely check that out. And let's get into if. >> Okay, so in its simplest form, you write the if function well as a function. That is the word if followed by an opening parenthesis and then a closing parenthesis. And inside the parenthesis, you can put one or more condition value pairs which are written similarly to a declaration. So that is a condition followed by a colon. then the value that should be used in case a condition is true and then a semicolon. Now it it might be confusing with all the columns and the semicolons, but what helps here is to split the code over multiple lines. >> It also looks a lot more clear when you see it in front of you versus hearing us talk about it. So I recommend you check out the resources in the show notes to actually play with this. >> Yeah. So follow along. If you have a code snippet open, great. You can follow along here. Say for example you want to conditionally color something. You'd start a color declaration and for its value you'd first write if followed by an opening pair and then a new line. And after that you write one or more condition value pairs each separated also by a new line. And then finally on the last line you still need to close the if statement which you do by typing the closing parenthesis followed by a semicolon that closes the declaration. >> That's a good call with the new lines. I do this too. So to reiterate the basic structure, it consists of three lines essentially. The first one reads the name of the property colon if and then that's what you're actually going to write the if statement in. You open parenthesis for everything inside the if statement. Then on the next line you have a condition with the value written just like a declaration. And then in the last line you close everything with that closing parenthesis of the statement. And then you have a semicolon because it's CSS. And then you move on to the next property value pair. [laughter] >> Exactly. Um, as for the conditions that you can use, there are three types of conditions you can check for and they're all done using functions. Um, first of all, you have the supports function which allows you to do a supports query. Secondly, you have a media function to check if a media condition matches. And then the third one you can use as a style function to do a style query on the element itself. And for example, if you want to check if the width is greater than 20 m, you would write the word media, open the parenthesis, the query part then, which is the width greater than 20 m, and then you close the parenthesis. So that's a media function with the check that goes inside of it, and you use that as a condition inside of an if function. Lastly, there's also the else keyword, which you can use, and it's also kind of a condition, but it's just a keyword. And yes, it does seem a bit weird that the else goes inside of the if function, but if you split it over multiple lines, it's very readable. >> It's like an if else statement kind of makes sense. It's not like a true if because there's not loops, but it's kind of like a statement. It's like a case function almost where you have different cases. >> So, let's do a real example. You mentioned uh you know, when something is larger than 20 m, you might want to style it some way. Let's say you want to give it a red background. uh when it's larger than 20 m otherwise a green background. So you'd write this declaration in your CSS rule as color colon if open parenthesis. Here we're going to do our new line trick. [laughter] Then the media condition check for 20 m which is written as the media function with with width greater than 20 m inside followed by a colon a value of red and a semicolon. Then >> a new line. >> Yes, a new line [laughter] with you on there. Um, and you can also have the else keyword, a colon, the value of green, which is our fallback value, and a semicolon. You can add another new line to close it all up, which is the closing parenthesis, and a semicolon, and you've got your CSS if statement. >> Yeah, again, if you couldn't follow here, like check out the source codes linked to in the the show notes, but I do think the new lines really help here. Now, to be clear, they are not mandatory, but they make it more readable for you as an author. So one of the other checks that I mentioned that you can do is a style query and you use this one to query the value of a custom property. The syntax for it is the function named style and inside of the parenthesis you put dash name of the variable and then uh the value that you want to compare to. So that's d-name of the variable a colon and then the value that it should have. This again is similar to how you write a declaration. >> Yeah. So I actually built a demo recently where I have this if statement querying these custom properties using the style function and I have like sort of a cambban type board with to-dos with active and inactive and I'm using the if statement to check for a style query with a value of status. So for each of these items for example I could have like a data attribute status and then I could check for that or here I'm converting them into a custom property. I'm using the adder function here and I'm checking for that style query inside of the if statement. And there's also an open issue for checking adder directly. But uh this is the way that I did it. Just converting it over for now. And then I'm checking if it's a status of pending. Give it a background of royal blue. If it's a status of complete, see green, else gray. And then um also I'm changing the border color, background color, and grid column. Uh, so I'm repositioning things inside of a CSS grid using the status. So that's where like the canban rearranging is. So I'm writing if styles- status pending, move it into the first column with one. If the status is complete, move into the second column, else third column. And you can now make this accessible with reading flow grid columns. Even though we're changing things in that grid visually, the DOM stays the same. But we now have a new CSS property also in Chrome 137 called reading flow that can actually read it in the visual order. So you can make it accessible that way even though you're changing stuff. So anyway, that's kind of one example of how you can use this with CSS custom properties and the style queries. Yeah, and I I really like that that demo that you shared because it shows that you can like put multiple conditions inside of. So like first you do the check for is the value of the custom property is it complete or is it and then on the second line you go oh but is it pending? So you can have multiple conditions in the if and you can also mix and match if you want. So you can like do a check if the media is larger than 20 amps then give it some value and then do a second check if if that one was false then do a second check like using a style query. So you can mix and match whatever you want. Do note though that the first one that evaluates to true, that one is going to return the value. So that one will determine the value that's going to be used. >> Yeah, I also have an article on developer.chrome.com about if I think like one of the examples I like is just an inline statement for like icon button size where you might want it um if any pointer fine that's using the media function then set it to 30 pixels but if it's a coarse pointer so else set it to 44 pixels. So you have like a minimum touch screen size for that button. This is just like one example of how writing if statements just make your CSS more concise where you can write this in one line of code instead of having to have a separate app media query and then using the same selector again and updating the width that way. So it's just everything you can do with if you could do now in CSS. It's just more visually concise and neater. And actually those style queries directly on the the element itself. That's a new thing. >> Yeah, it's so powerful. I really really really like them. But so um let's move on to the second part of this episode which is about custom functions. >> Oh yeah, custom functions. These are exciting. >> Yeah, and I I saw the blog post on your blog. Some really good examples in there. They are going to definitely change how I write my CSS from now on. >> Yes, same. >> So as the name implies, custom functions allow you to write custom functions which take some input and produce a single output value. Yeah, they're honestly kind of more like complex CSS custom properties. But while custom properties only allow you to store a single value that you access with var, a custom function can run logic, accept arguments, which can also have default values, and it spits out a new value. So you call them with a function syntax based on the name that you gave it, which could be something like d- my function and then open close parenthesis instead of the var syntax that needs to surround a CSS custom property. And to define a custom function in CSS, you use the new CSS function at rule followed by the name of that function which takes a dashed ident. So yeah, it needs to start with two dashes. And after the name of the function goes a pair of parentheses in which you can put the arguments separated by commas and those arguments too are dashed items just like custom properties. And finally you open a block with curly braces in which you can put your logic. Now that logic can contain a lot of things like media queries, but what should definitely be in there is a result descriptor is a thing that holds a result when calling the function. >> An example that comes straight from the spec is the negative function which allows you to make a given length a negative value. So you give it a length value and it returns the negation of it. So the syntax to declare this at function is basically dash dash negative or whatever you want to call it and then you open a parenthesis. You have a dash value inside of there. Close the parenthesis. Open your curly brace. Then you have the result colon. And here we're going to have a calc statement. So calc and inside of the calcgative -1* var- value. So you want to be calling the arguments inside of your function- value using the var statement var for custom properties [laughter] and then you're multiplying that by negative one. So that's what it looks like. app function d- negate open close parenthesis inside of that d-v value open and close parenthesis inside of that result colon cal negative 1 times var open close parenthesis d-v valueue and >> again it's it's it sounds complicated but go check out the show notes for some examples once you see it it immediately clicked when I saw it it's very straightforward >> it makes so much more sense when you see it but then also when you use it it's very straightforward because you just call the name of the function with the value that you want to use inside of it. So it makes it a lot easier to use possible complex CSS. >> Yes. So looking at that negative function that you just mentioned, you can you can call it anywhere a value is allowed. So if you want to set the margin to a negative 8 pixels using that function, you write a declaration that reads margin colon and then d- negate to call the function, open the parenthesis, you put 8 px in there as the argument, and then you finally close the parenthesis. >> Super easy. And this is probably like one of the simplest possible examples, but you can use CSS functions to really hide a lot of more complex CSS behind the function and then just access it by name. For example, Tam of FIF has done some really complex CSS mastery and just magicy. I love following their work and uh they showed converting I think it was for some kind of geometric calculations for animation like a bunch of calculations but hiding it behind a function so that you just call it and I think you put the arc and the radius inside the functions the arguments and you don't even have to know what's behind the scenes. So it's just a way to really clean up your CSS code. >> Yeah, I'm so happy that there are clever people out there who can like hide away all the math behind just the name of a function that you have to call and be done with it. >> Plus one. So um as just mentioned a function can take arguments and if the function accepts more than one argument you separate them by commas in the function parameter list. If you want you can also specify the type of the argument and also give it a default value. The type that you want to specify goes immediately after the name of the argument and the default value is added to it using a colon. So for example, if you want to make sure that d-archc is a length with a default value of one pixel, then you type d-archc space open the angled bracket then the word length and then a closing angled bracket to set the type and then you type a colon and then space 1px to give it the default value. So one thing that I saw at CSSA this year was this conditional border radius technique that was originally I think created by Nan Go at Facebook and popularized by Ahmad Shaded who broke it down um in a blog post. And so I wanted to functionize it. And what this does is essentially based on how much space there is around a box it will or won't have a border radius. So that means that you could have divs or boxes inside of your UI that have a border radius and then when you have a smaller viewport where they hit the edges or are about to hit the edges, you remove the border radius so there's not like this weird radius before you see the edge of the viewport. So it kind of goes like full width. So I converted this into a function and the function takes two arguments. The first of which is the radius, the border radius that you want to apply. And the second is the edge distance that you want to start changing from the radius to 0 pixels where you're removing the radius. And so I have a function called conditional radius. So it's app function d-conditional radius and then in parenthesis the two arguments are d- radius, and then d- edgeist. And I have a colon here for a default value of four pixels. So it's d- edge dis col four pixels and that's going to be the default if you don't have a second argument. So you could either have two arguments which is the radius and a custom edge distance like if you want to be zero pixels right at the edge or if you only have one argument then it takes the default and then it applies the value and then when you call it I could have a box and then say border radius d-conditional radius one rem and it does all the logic behind the scenes. The logic is like you clamp and you start at zero pixels and then inside of the clamp you do a calc with 100 VW minus the edge distance minus 100% times a really big number to represent the largest viewport size. So it's like one to the fifth power and then uh the radius. So you don't have to see all of that. You can just say I want the conditional radius at one and then possible distance. >> Yeah. Yeah, first of all, like it's a very clever technique and also it makes it much more readable because you functionized it into the function named conditional radius like I get what it does from the name. Whereas if I had to look at the clamp function and it was like uh what again is this? So you probably needed a comment somewhere in your source to like clarify what it meant. And now with the function so so cool. Yeah. So um so far I've only mentioned the result descriptor that goes into the function body but you can put much much more in that such as a media query to give the result descriptor a value based on that media query. >> Yeah. Like another example in this blog post is uh this layout sidebar where I have a function called layout sidebar. It's accepting a sidebar width and what it does is essentially changes the layout if you have some main content in a sidebar. So you could do like large CSS changes. It's not just for these like small things like colors and sizes. And then inside that function I have the function statement, the declaration at function layout sidebar. I have my argument sidebar width my default value of 20 ch. And then inside that I'm returning the result of 1 fr. So that's going to be the space that it takes up 1 fr. But then inside of that after that result that's essentially like an early return unless you have an app media where I have an app media width greater than 640 pixels. Then I have a second result which is result bar the sidebar width that's coming from the argument auto. So that's essentially saying I want to have this sidebar be either my uh set value or my default value of 20 CH and then take up that much space with the rest of the layout being auto for the main content or I want it to be 1 FR aka one fractional unit that goes across the entire viewport width so that it stacks on top of the main content. and then on a parent element called layout or wrapper. I would use this within grid template columns and then it would set the grid template columns for that entire layout to either 1fr or set it to be two columns which would be the sidebar width and auto. And so you could really like use this in a lot of different ways. It's a very uh extensible API. >> Yeah. Well, and what I really like here is that like the code that goes inside of the at function body, it's just regular CSS. like general rules like source order still apply. You can write the media query in there to override the thing. The only thing that is different or new there is that inside of this function body you can use this special result descriptor to contain the result that you that you wanted to return. And that's also by the way one of the pieces of feedback that I often hear from people like okay why is it called result and not return well the answer there is the example that you just gave because you can give that result a value to start with and then override it later on. based on the media query. If we had named it return, one might expect that it would stop executing after it sees us lines, something like programming language, like JavaScript do, like an early return. But that is not the case here. >> Yes, and this is still a bunch of declarations that go into a function body. And it's that last set of declarations that sets the value for the result that ends up being returned by the function. So, it's not quite the same as a return. >> Yeah. One last thing before we wrap things up here. Um, you know what I really like about these function parameters? >> What do you like about the function parameters? Tell me more. >> Yeah, that they are just like custom properties. They are double dashed and you access them through var. And that's not a coincidence because it opens up some opportunities. Say, for example, that you write var-gap inside of your function body. It will first try and look it up as one of the local variables that were declared inside of the function body. If it can't find it there, it'll turn to the function arguments list and try to get it from that list. And if no argument with that name exists or it does exist, but it has an value of initial, then it will try and get the value from a custom property with that same name, but from the element where you called the function. >> Oh, interesting. Okay, so this makes sense because you used that in a custom light dark function that you wrote on your blog that works with any type of value instead of being limited to colors, which makes light dark a lot more extensible. I've wanted this for a while. But in the function body there, you're using var- scheme, which isn't passed into the function as an argument at all. Instead, it's a custom property declared on the element itself. And what I also like is that you're using an if statement with a style condition inside of the function body to check the value of the scheme property. >> Yeah. How's that for a final example we're covering here, right? You can totally mix custom functions and the inline if function. It's it's very powerful stuff. >> Oh, CSS is so good. >> So good. Well, that's it for this episode. I think I've covered it all or at least the basics to get you started. I'm very excited to see what custom functions you all are going to come up with. My hunch is that over time we'll end up having some functions.css files or libraries getting created over time and that you can drag them from project to project. >> Yeah, I think that we'll see reset.css and we'll have like a utils.css and that's going to be something that you kind of get used to using. You're going to have your own custom functions and I like that world. I'm excited for that world. >> Same. And also definitely go check out the show notes for the links that we mentioned to demos and resources because it's a lot easier to just see it in front of you and get a chance to play with it. >> Yeah. And if you have any CSS questions, we would love to answer them on the show. So please tweet at us or post on Blue Sky with the hashtag CSSodcast. >> I'm Yuna on Twitter and I'm yuna.im on Blue Sky >> and I'm at brahum. us on blue sky and at brahies.frontend.social on mastadon. I'm also on Twitter, but the main action is happening on Blue Sky. >> If you like this show, please give us a review on your podcast app of choice or share our show with a friend because those reviews help other people discover our show and that means that we get more time to make episodes and deliver better content for you. >> Thank you for your questions and looking forward to seeing them. >> We'll see you next time. >> Bye. [music] >> [music]