Core features and syntax details
The @function rule might look fancy at first, but once you understand the basics, it’s actually very logical. Let’s go through each important part step by step.
1. Naming rules
When you create a function in CSS, the name must start with two dashes --, just like custom properties. For example:
@function --my-function(--value) {
result: calc(var(--value) * 2);
}
You can call it whatever you like, as long as:
It starts with --
It only contains letters, numbers, and hyphens
It’s unique within your stylesheet (you can’t have two functions with the same name in the same layer)
So names like --double, --spacing, or --theme-color are all valid.
2. Parameters (with or without default values)
Parameters are like “inputs” your function can take. For example, in this function:
@function --add-margin(--size) {
result: calc(var(--size) + 10px);
}
--size is the parameter. Whatever you pass in will replace it.
You can also give parameters default values, so your function still works even if you don’t pass anything in:
@function --add-margin(--size: 10px) {
result: calc(var(--size) + 10px);
}
.box {
margin: --add-margin(); /* uses default 10px */
}
That’s super useful when you want your function to have a “fallback” value.
3. Return values (the result: descriptor)
The line that starts with result: tells CSS what your function should return. Whatever value you write after result: becomes the function’s output. Example:
@function --double(--x) {
result: calc(var(--x) * 2);
}
You can even use other functions, variables, or complex logic inside the result.
If you write more than one result: inside the same function (for example, under different media queries), the last one wins, just like in normal CSS.
4. Types and type checking
CSS functions can also define types for parameters. This means you can tell CSS what kind of value you expect, like a number, color, or length, and it’ll check that for you. For example:
@function --fade(--color <color>, --opacity <number>: 0.5) returns <color> {
result: color-mix(in srgb, var(--color), transparent var(--opacity));
}
Here’s what’s happening:
--color must be a color
--opacity must be a number, and defaults to 0.5 if not provided
returns <color> means the result must also be a color
This makes your CSS functions more predictable and prevents weird results.
5. Logic inside functions
This is where things get interesting. CSS functions can also include conditional logic and media or container queries inside them. For example, you can use the new if() function to make decisions:
@function --theme-color(--is-dark) {
result: if(var(--is-dark) == true, black, white);
}
That means your function can return different values depending on conditions, like theme, screen size, or container width.
You can even use media or container queries inside your function:
@function --responsive-font(--base-size) {
result: var(--base-size);
@media (min-width: 800px) {
result: calc(var(--base-size) * 1.2);
}
}
This function increases the font size only on larger screens — all inside the function!
6. Calling functions
Once your function is defined, using it is simple: just call it by name, with parentheses:
.element {
width: --double(30px);
}
You can use it anywhere a normal CSS value would go. For example, inside other functions, within calc(), in shorthand properties, and so on.
7. Cascade and layer behavior
Just like CSS variables, functions also follow the cascade. That means if you define two functions with the same name, the one that appears later (or in a higher-priority layer) will take effect. For example:
@layer defaults {
@function --color(--name) {
result: blue;
}
}
@layer theme {
@function --color(--name) {
result: red;
}
}
.box {
color: --color();
}
Here, the theme layer overrides the defaults one, so the color becomes red. This lets you customize or override functions across different layers, which is perfect for theming or large design systems.
Practical examples
Now that you know how @function works, let’s look at some real examples that show how powerful and useful it can be in everyday CSS.
These are simple, practical cases that solve common problems, like converting units, adjusting colors, or making text fluid.
1. Unit conversion (px → rem)
Have you ever found yourself constantly converting pixels to rem? This function can do that math for you automatically.
@function --to-rem(--px <length>) returns <length> {
result: calc(var(--px) / 16);
}
.text {
font-size: --to-rem(24px);
}
Now, when you pass in a pixel value like 24px, the function divides it by 16 (the usual base font size) and you get 1.5rem.
Now you can use --to-rem() anywhere, and your conversions are automatic and consistent.
2. Color manipulation (Add transparency)
If you need to make a color slightly transparent without repeating the same rgba() formulas, here’s a custom function for that:
@function --with-opacity(--color <color>, --alpha <number>: 0.8) returns <color> {
result: color-mix(in srgb, var(--color), transparent var(--alpha));
}
.card {
background-color: --with-opacity(#007bff, 0.2);
}
In the code above, --color is your base color, --alpha is how transparent you want it (default is 0.8). The function then combines the color with transparency to produce a softer version.
You can reuse this function for any color and it’s perfect for hover states, overlays, and subtle backgrounds.
3. Fluid Typography
One of the most common needs in modern CSS is the ability to scale font sizes with the screen width. Here’s how you can make your own function for that:
@function --fluid-type(--min <length>, --max <length>) returns <length> {
result: clamp(var(--min), 2vw + 1rem, var(--max));
}
h1 {
font-size: --fluid-type(1.5rem, 3rem);
}
The function above uses clamp() to set a min, preferred, and max value. As the viewport grows, the font scales smoothly between those limits.
This keeps your text readable and beautiful on any screen, all wrapped up in one neat, reusable function.
Each of these examples demonstrates how @function enables you to reuse logic, avoiding code repetition and maintaining clean, readable, and flexible CSS.