@function
The @function
CSS at-rule enables defining CSS custom functions. Once defined, a custom function can be called using the <dashed-function>
syntax (for example, --my-function(30px, 3)
) within any property value.
Syntax
@function --function-name(<function-parameter>#?) [returns <css-type>]? {
<declaration-rule-list>
}
<function-parameter> = --param-name <css-type>? [ : <default-value> ]?
The different parts of the @function
syntax are as follows:
--function-name
-
The identifying name of the function, a
<dashed-ident>
that starts with--
and is followed by a valid, user-defined identifier. It is case-sensitive. <function-parameter>#?
Optional-
Zero or more function parameter definitions. Multiple parameter definitions are separated by commas. Each parameter consists of:
--param-name
-
A CSS custom property name to identify the parameter, a
<dashed-ident>
that starts with--
and is followed by a valid, user-defined identifier. It is case-sensitive. Function parameters can be considered custom properties that are locally scoped to the function body. <css-type>
Optional-
A CSS data type or a
type()
function that defines the accepted data type(s) for the parameter. If this is not specified, any data type will be valid for the parameter (the same as specifyingtype(*)
), although bear in mind that it may still be incompatible with the logic in the<declaration-rule-list>
and produce an invalid result. <default-value>
Optional-
A CSS value specifying the default value to assign to the parameter if it is not specified when the function is called. This value must be valid according to the
<css-type>
if specified. The default value is separated from the other parts of the parameter definition with a colon (:
).
[returns <css-type>]?
Optional-
A CSS data type or a
type()
function, preceded by the keywordreturns
, which defines the accepted return type(s) for the parameter. If this is not specified, any data type will be valid for the parameter (the same as specifyingreturns type(*)
), although bear in mind that the function will be invalid if the return type does not match the type produced by theresult
descriptor. <declaration-rule-list>
-
One or more CSS declarations or at-rules that define the body of the function, which contains its logic. Included declarations can include:
- CSS custom properties, which are locally scoped to the function body.
- The
result
descriptor, either directly inside the@function
at-rule, or inside a nested at-rule.
Descriptors
result
-
A valid property value that defines the result returned by the CSS custom function. The expression contained in the value is evaluated, and the result is returned.
Description
CSS custom functions allow you to define reusable sections of logic that will return different values depending on the parameters they accept as inputs and the logic defined inside the function body.
A typical CSS function looks like this:
@function --transparent(--color, --alpha) {
result: oklch(from var(--color) l c h / var(--alpha));
}
The function has a name of --transparent
and takes two custom properties as parameters: --color
and --alpha
, which can be used locally inside the function body. The body contains a single line, which is a result
descriptor that defines the value returned by the function. The value of the result
descriptor uses CSS relative color syntax to convert the input --color
value into an oklch()
color with the alpha channel value specified in the input --alpha
value.
You can then call this function anywhere you want to produce a semi-transparent version of an existing color, for example:
section {
--base-color: #faa6ff;
background-color: --transparent(var(--base-color), 0.8);
}
The function is called by using <dashed-function>
syntax, which is the function name with parentheses on the end, and the desired argument values specified inside.
Note:
If multiple CSS functions are given the same name, the function in the stronger cascade @layer
wins. If all of them are in the same layer, the function defined last in the source order wins.
Passing comma-containing values as arguments
In the next example, the --max-plus-x()
function expects to be passed a comma-separated list of lengths and a single length as arguments. It uses the CSS max()
function to determine which of the list of lengths is the largest, adds it to the single length, then returns the result.
@function --max-plus-x(--list <length>#, --x <length>) {
result: calc(max(var(--list)) + var(--x));
}
Because the first argument needs to be a comma-separated list, you can include it when calling the function by wrapping the value in curly braces:
div {
width: --max-plus-x({1px, 7px, 2px}, 3px); /* 10px */
}
Specifying data types
It is possible to specify data types for the function parameters and return types. For example:
@function --transparent(--color <color>, --alpha <number>) returns <color> {
result: oklch(from var(--color) l c h / var(--alpha));
}
Now the function will only produce a valid value if the input arguments are a <color>
and a <number>
, respectively, and the result
is a <color>
. If not, for example:
section {
--base-color: #faa6ff;
background-color: --transparent(var(--base-color), 50%);
}
then the value will be invalid, and the declaration will be ignored.
You can specify multiple accepted data types using a type()
function and some OR logic, for example:
@function --transparent(--color <color>, --alpha type(<number> | <percentage>))
returns <color> {
result: oklch(from var(--color) l c h / var(--alpha));
}
With this adjustment, the --transparent(var(--base-color), 50%)
value will now be valid.
Specifying default values
You can also specify default values for parameters, after a colon at the end of their definition. For example:
@function --transparent(--color <color>, --alpha <number>: 0.8) returns <color> {
result: oklch(from var(--color) l c h / var(--alpha));
}
The --alpha
parameter's default value is now 0.8
. If you want to use this value, you can omit the second argument when calling the function:
section {
--base-color: #faa6ff;
background-color: --transparent(var(--base-color));
}
Note: If an invalid value is passed in as a function argument and a default value is specified in that parameter definition, the invalid value will be ignored, and the default value will be used instead.
Including custom properties inside functions
As we've already seen, function parameters are defined as custom properties, which are then available inside the function body.
You can also specify custom properties inside the function body that will act as locally-scoped constants. In the following example, we define a function called --anim-1s()
, which returns an animation
shorthand value where the duration and easing values are always the same, and only the animation name and count are varied.
@function --anim-1s(--animation, --count) {
--duration: 1s;
--easing: linear;
result: var(--animation) var(--duration) var(--count) var(--easing);
}
This kind of usage allows you to write easier, more expressive syntax for animations, provided you know that you always want the duration and easing function to be the same:
animation: --anim-1s(bounce, 2);
It is also worth noting that you can call one custom function from inside another one. In such cases, a custom function can access local variables and function parameters from functions higher up in the call stack. Here, the outer function's parameter and local custom property will be available inside the scope of the inner function:
@function --outer(--outer-arg) {
--outer-local: 2;
result: --inner();
}
@function --inner() returns <number> {
result: calc(var(--outer-arg) + var(--outer-local));
}
div {
z-index: --outer(1); /* 3 */
}
In addition, custom properties defined on the same element the custom function is being called on will be available to it:
@function --double-z() returns <number> {
result: calc(var(--z) * 2);
}
div {
--z: 3;
z-index: --double-z(); /* 6 */
}
If a custom property of the same name is defined in multiple places, function parameters override custom properties defined on the same element, and local custom properties defined inside the function body override both. In the following example, the --add-a-b-c()
function uses the --a
property from the div
rule's custom property, the --b
property from the function parameter, and the --c
local custom property.
@function --add-a-b-c(--b, --c) {
--c: 300;
result: calc(var(--a) + var(--b) + var(--c));
}
div {
--a: 1;
--b: 2;
--c: 3;
z-index: --add-a-b-c(20, 30); /* 321 */
}
Including complex logic
You can include more complex logic in functions using constructs such as @media
at-rules and if()
functions. For example, the next function takes two arguments, one for a narrow-screen layout, and one for a wide-screen layout. It returns the latter by default, but returns the former when the viewport width is less than 700px
wide, as detected using a media query.
@function --narrow-wide(--narrow, --wide) {
result: var(--wide);
@media (width < 700px) {
result: var(--narrow);
}
}
You can include multiple result
descriptors to express different results for different logic outcomes.
Note:
CSS functions behave in same way as the rest of CSS in regard to conflict resolution — last in source order wins. Therefore, in the above function, the result
is var(--wide)
unless the media query test returns true, in which case it is overriden by var(--narrow)
.
There are no early returns in CSS functions like there are in JavaScript functions. In the above function, if the media query was written first before the single result
line, the result
would always be var(--wide)
because it would override var(--narrow)
in cases where the media query test returns true.
We could rewrite the function to use an if()
function instead:
@function --narrow-wide(--narrow, --wide) {
result: if(media(width < 700px): var(--narrow) ; else: var(--wide));
}
Formal syntax
@function =
@function <function-token> <function-parameter>#? ) [ returns <css-type> ]? { <declaration-rule-list> }
<function-parameter> =
<custom-property-name> <css-type>? [ : <default-value> ]?
<css-type> =
<syntax-component> |
<type()>
<default-value> =
<declaration-value>
<syntax-component> =
<syntax-single-component> <syntax-multiplier>? |
'<' transform-list '>'
<type()> =
type( <string> )
<syntax-single-component> =
'<' <syntax-type-name> '>' |
<ident>
<syntax-multiplier> =
'#' |
'+'
<syntax-type-name> =
angle |
color |
custom-ident |
image |
integer |
length |
length-percentage |
number |
percentage |
resolution |
string |
time |
url |
transform-function
Examples
For more complete example walkthroughs, see our Using CSS custom functions guide
Basic @function
usage
This example shows a basic function that doubles the value passed into it.
HTML
The markup features a <div>
element with a <p>
inside it containing some text content.
<div>
<p>Some content</p>
</div>
CSS
In our styles we first define the CSS custom function. The function is called --double
, and accepts a single parameter of any type, which we've called --value
. Inside the function body, we include a result
descriptor that uses the calc()
function to double the passed argument:
@function --double(--value) {
result: calc(var(--value) * 2);
}
Next, we define a --base-spacing
custom property with a value of 10px
. We assign that property to be the value of our border-radius
, but then double it for our padding
value using our --double()
custom function.
div {
--base-spacing: 10px;
border-radius: var(--base-spacing);
padding: --double(var(--base-spacing));
width: 50%;
background-color: magenta;
}
Result
Specifications
Specification |
---|
CSS Functions and Mixins Module # function-rule |
Browser compatibility
See also
- CSS custom properties
<dashed-function>
data typetype()
function- Using CSS custom functions
- CSS custom functions and mixins module