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]