Functions In CSS?! | CSS-Tricks
CSS-Tricks
·
Mar 03, 2025
·
article
_A much-needed disclaimer:_ **You (kinda) can use functions now!** I know, it isn’t the most pleasant feeling to finish reading about a new feature just for the author to say “And we’ll hopefully see it in a couple of years”. Luckily, right now you can use an ([incomplete](#aa-whats-missing)) version of CSS functions in Chrome Canary behind an experimental flag, although who knows when we’ll get to use them in a production environment.
### Arguments, defaults, and returns!
I was drinking coffee when I read the news on Chrome prototyping functions in CSS and… I didn’t spit it or anything. I was excited, but thought “functions” in CSS would be just like [mixins in Sass](https://sass-lang.com/documentation/at-rules/mixin/) — you know, patterns for establishing reusable patterns. That’s cool but is really more or less syntactic sugar for writing less CSS.
But I looked at the example snippet a little more closely and that’s when the coffee nearly came shooting out my mouth.

From [Bramus in Bluesky](https://bsky.app/profile/bram.us/post/3lhphp644rc2a)
Arguments?! Return values?! That’s worth spitting my coffee out for! I had to learn more about them, and luckily, the spec is clearly written, which[ you can find right here](https://drafts.csswg.org/css-mixins-1/). What’s crazier, you can use functions right now in Chrome Canary! So, after reading and playing around, here are my key insights on what you need to know about CSS Functions.
### What exactly is a function in CSS?
I like this definition from the spec:
> Custom functions allow authors the same power as custom properties, but parameterized
They are used in the same places you would use a custom property, but functions return different things depending on the argument we pass. The syntax for the most basic function is the `@function` at-rule, followed by the name of the function as a `` \+ `()`
```
@function --dashed-border() {
/* ... */
}
```
A function without arguments is like a custom property, so meh… To make them _functional_ we can pass arguments inside the parenthesis, also as ``s
```
@function --dashed-border(--color) {
/* ... */
}
```
We can use the `result` descriptor to return something based on our argument:
```
@function --dashed-border(--color) {
result: 2px dashed var(--color);
}
div {
border: --dashed-border(blue); /* 2px dashed blue */
}
```
> We can even use defaults! Just write a colon (`:`) followed by the default value for that argument.
>
> ```
> @function --dashed-border(--color: red) {
> result: 2px dashed var(--color);
> }
>
> div {
> border: --dashed-border(); /* 2px dashed red */
> }
> ```
>
> CodePen Embed Fallback
>
> This reminds me of Adam Argyle’s experiment on [a functional CSS concept](https://nerdy.dev/functional-css-concept).
### Functions can have type-checking
Functions can have type-checking for arguments and return values, which will be useful whenever we want to interpolate a value just like we do with variables created with `[@property](https://css-tricks.com/almanac/rules/p/property/)`, and once we have [inline conditionals](https://css-tricks.com/if-css-gets-inline-conditionals/), to make different calculations depending on the argument type.
To add argument types, we pass a [syntax component](https://drafts.csswg.org/css-values-5/#typedef-syntax-component). That is the type enclosed in angle brackets, where color is `` and length is ``, just to name a couple. There are also syntax multipliers like plus (`+`) to accept a space-separated list of that type.
```
@function --custom-spacing(--a ) { /* ... */ } /* e.g. 10px */
@function --custom-background(--b ) { /* ... */ } /* e.g. hsl(50%, 30% 50%) */
@function --custom-margin(--c +) { /* ... */ } /* e.g. 10px 2rem 20px */
```
If instead, we want to define the type of the return value, we can write the `returns` keyword followed by the syntax component:
```
@function --progression(--current, --total) returns {
result: calc(var(--current) / var(--total) * 100%);
}
```
Just a little exception for types: if we want to accept more than one type using the syntax combinator (|), we’ll have to enclose the types in a `type()` wrapper function:
```
@function --wideness(--d type( | )) { /* ... */ }
```
### Functions can have list arguments
While it [doesn’t currently seem to work in Canary](#aa-whats-missing), we’ll be able in the future to take lists as arguments by enclosing them inside curly braces. So, this example from the spec passes a list of values like `{1px, 7px, 2px}` and gets its maximum to perform a sum.
```
@function --max-plus-x(--list, --x) {
result: calc(max(var(--list)) + var(--x));
}
div {
width: --max-plus-x({ 1px, 7px, 2px }, 3px); /* 10px */
}
```
I wonder then, will it be possible to select a specific element from a list? And also define how long should the list should be? Say we want to only accept lists that contain four elements, then select each individually to perform some calculation and return it. Many questions here!
### Early returns aren’t possible
That’s correct, early returns aren’t possible. This isn’t something defined in the spec that hasn’t been prototyped, but something that simply won’t be allowed. So, if we have two returns, one enclosed early behind a `@media` or `@supports` at-rule and one outside at the end, the last result will always be returned:
```
@function --suitable-font-size() {
@media (width > 1000px) {
result: 20px;
}
result: 16px; /* This always returns 16px */
}
```
We have to change the order of the returns, leaving the conditional `result` for last. This doesn’t make a lot of sense in other programming languages, where the function ends after returning something, but there is a reason the C in CSS stands for Cascade: this order allows the conditional result to override the last result which is very CSS-y is nature:
```
@function --suitable-font-size() {
result: 16px;
@media (width > 1000px) {
result: 20px;
}
}
```
### Imagining the possibilities
**Here I wanted everyone to chip in and write about the new things we could make using functions**. So the team here at CSS-Tricks put our heads together and thought about some use cases for functions. Some are little helper functions we’ll sprinkle a lot throughout our CSS, while others open new possibilities. Remember, all of these examples should be viewed in Chrome Canary until support expands to other browsers.
Here’s a basic helper function from Geoff that sets fluid type:
```
@function --fluid-type(--font-min, --font-max) {
result: clamp(var(--font-min), 4vw + 1rem, var(--font-max));
}
h2 {
font-size: --fluid-type(24px, 36px);
}
```
CodePen Embed Fallback
This one is from Ryan, who is setting the width with an intrinsic container function — notice the default arguments.
```
@function --intrinsic-container(--inline-margin: 1rem, --max-width: 60ch) {
result: min(100% - var(--inline-margin), var(--max-width));
}
```
CodePen Embed Fallback
> And check out this second helper function from Ryan to create grid layouts:
>
> ```
> @function --layout-sidebar(--sidebar-width: 10ch) {
> result: 1fr;
>
> @media (width > 640px) {
> result: fit-content(var(--sidebar-width)) minmax(min(50vw, 30ch), 1fr);
> }
> }
> ```
>
> This is one of those snippets I’m always grabbing from Steph Eckles’ [smolcss site](https://smolcss.dev/), and having a function would be so much easier. Actually, _most_ of the snippets on Steph’s site would be awesome functions.
>
> CodePen Embed Fallback
This one is from _moi_. When I made that demo using [tan(atan2()) to create viewport transitions](https://css-tricks.com/typecasting-and-viewport-transitions-in-css-with-tanatan2/), I used a helper property called `--wideness` to get the screen width as a decimal between `0` to `1`. At that moment, I wished for a function form of `--wideness`. As I described it back then:
> You pass a lower and upper bound as pixels, and it will return a `0` to `1` value depending on how wide the screen is. So for example, if the screen is `800px`, `wideness(400px, 1200px)` would return `0.5` since it’s the middle point
I thought I would never see it, but now I can make it myself! Using that wideness function, I can move an element through its `[offset-path](https://css-tricks.com/almanac/properties/o/offset-path/)` as the screen goes from `400px` to `800px`:
```
.marker {
offset-path: path("M 5 5 m -4, 0 a 4,4 0 1,0 8,0 a 4,4 0 1,0 -8,0"); /* Circular Orbit */
offset-distance: calc(--wideness(400, 800) * 100%); /* moves the element when the screen goes from 400px to 800px */
}
```
CodePen Embed Fallback
### What’s missing?
According to [Chrome’s issue on CSS Functions](https://issues.chromium.org/issues/325504770), we are in a super early stage since we _cannot_:
* …use local variables. Although I tried them and they seem to work.
* …use recursive functions (they crash!),
* …list arguments,
* …update a function and let the appropriate styles change,
* …use `@function` in cascade layers, or in the CSS Object Model (CSSOM),
* …use “the Iverson bracket functions … so any `@media` queries or similar will need to be made using helper custom properties (on `:root` or similar).”
After reading what on earth an [Iverson bracket](https://en.wikipedia.org/wiki/Iverson%5Fbracket) is, I understood that we currently can’t have a return value behind a `@media` or `@support` rule. For example, this snippet from the spec shouldn’t work:
```
@function --suitable-font-size() {
result: 16px;
@media (width > 1000px) {
result: 20px;
}
}
```
Although, upon testing, it seems like it’s supported now. Still, we can use a provisional custom property and return it at the end if it isn’t working for you:
```
@function --suitable-font-size() {
--size: 16px;
@media (width > 600px) {
--size: 20px;
}
result: var(--size);
}
```
CodePen Embed Fallback
What about mixins? Soon, they’ll be here. According to the spec:
> At this time, this specification only defines custom functions, which operate at the level of CSS values. It is expected that it will define “mixins” later, which are functions that operate at the style rule level.
### In conclusion…
I say it with confidence: **functions will bring an enormous change to CSS**, not in the sense that we’ll write it any differently — we won’t use functions to center a `
---
[Functions in CSS?!](https://css-tricks.com/functions-in-css/) originally published on [CSS-Tricks](https://css-tricks.com), which is part of the [DigitalOcean](https://try.digitalocean.com/css-tricks/?utm%5Fmedium=rss&utm%5Fsource=css-tricks.com&utm%5Fcampaign=family%5F&utm%5Fcontent=) family. You should [get the newsletter](https://css-tricks.com/newsletters/).
https://css-tricks.com/functions-in-css/