1. Introduction
This section is not normative.
Note: 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.
Custom properties give authors a lot of power to define useful, sometimes complex values in one place, and then re-use them across their stylesheet. They can vary across the document, or based on Media Queries or other conditionals, making them very flexible and responsive.
However, their values are fixed at the point they’re defined, unable to be changed except by fully overriding their previous definition: a --shadow: 2px 2px var(--shadow-color) declaration takes its --shadow-color value from the element it’s declared on, and later changes to --shadow-color on descendant elements don’t alter the value of --shadow for them; they continue to use the shadow color defined where --shadow was defined. This is a common source of confusion for authors making heavy use of composite variables like this.
Custom functions allow authors the same power as custom properties, but parameterized: they have the same flexibility and conditionality as a custom property definition, but take values from other custom properties (or explicitly as arguments) at the point of use. For example, instead of a --shadow custom property, a --shadow() custom function could be defined instead, like:
@function --shadow ( --shadow-color <color> : inherit) { /* If --shadow-color argument isn't passed, or doesn't successfully parse as a <color>, try to use the --shadow-color *property* from the element instead */ /* var(--shadow-color) refers to the --shadow-color parameter, rather than a custom property, but can still use a fallback value as normal */ result:2 px 2 px var ( --shadow-color, black); } .foo{ --shadow-color : blue; box-shadow : --shadow (); /* produces a blue shadow */ /* or just */ box-shadow:--shadow ( blue); }
2. Defining Custom Functions
A custom function can be thought of as an advanced custom property, which instead of being substituted by a single fixed value, computes its substitution value based on function parameters and the value of custom properties at the point it’s invoked. Rather than the var() syntax that custom properties use for substitution, custom functions are invoked by <dashed-function> syntax, allowing additional values to be passed as arguments.
@function --negative ( --value) { result : calc ( -1 *var ( --value)); }
Then, that function can be referenced with --negative() in some declaration:
html{ --gap : 1 em ; padding : --negative ( var ( --gap)); /* or by passing the value explicitly, like: */ padding:--negative ( 1 em ); }
<dashed-function>s are arbitrary substitution functions, like var(). Their presence in a property’s value causes it to be assumed valid at parse time, and only evaluated and parsed at computed-value time, after arbitrary substitution has occurred.
2.1. The @function Rule
The @function rule defines a custom function, and consists of a name, a list of parameters, a function body, and optionally a return type described by a syntax definition.
Each function parameter consists of a name (<custom-property-name>); optionally a parameter type, described by a syntax definition; and optionally a default value.
<@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> <type()> = type( <syntax> )
If a default value and a parameter type are both provided, then the default value must parse successfully according to that parameter type’s syntax. Otherwise, the @function rule is invalid.
2.1.1. The Function Preamble
The <function-token> production must start with two dashes (U+002D HYPHEN-MINUS), similar to <dashed-ident>, or else the definition is invalid.
The name of the resulting custom function is given by the name of the <function-token>, the optional function parameters are given by the <function-parameter> values (defaulting to an empty set), and the optional return type is given by the <css-type> following the returns keyword (defaulting to type(*)).
@function --foo ( --a <length>) { /* ... */ } @function --foo ( --a <color>) { /* ... */ } @function --foo ( --a <length>+) { /* ... */ }
However, any <syntax> that requires a <syntax-combinator> needs to be wrapped in the type() function:
@function --foo ( --atype ( <number> | <percentage>)) { /* ... */ }
The name of a @function rule is a tree-scoped name. If more than one @function exists for a given name, then the rule in the stronger cascade layer wins, and rules defined later win within the same layer.
If the function parameters contain the same <custom-property-name> more than once, then the @function rule is invalid.
2.1.2. The Function Body
The body of a @function rule accepts conditional group rules, such as @media. Additionally, it accepts the following descriptors:
-
The result descriptor, which determines the result of evaluating the function. If no result descriptor exists, the function is valid, but always returns the guaranteed-invalid value.
-
Custom properties, providing local variables.
Unknown descriptors are invalid and ignored, but do not make the @function rule itself invalid.
2.2. The result Descriptor
Name: | result |
---|---|
For: | @function |
Value: | <declaration-value>? |
Initial: | n/a (see prose) |
The result descriptor defines the result of evaluating the custom function defined by its @function rule. Using var() functions, it can reference function parameters, local variables, as well as other custom functions via <dashed-function>s.
The result descriptor itself does not have a type, but its resolved value is type-checked during the substitution of a <dashed-function>.
2.3. Arguments & Local Variables
This section is non-normative.
Within a custom function’s function body, the var() function can access local variables (the custom properties defined in the function body), function parameters (the values passed to the function, or set to default values), and custom properties defined at the call site (an element, or another custom function).
In that list, earlier things "win" over later things of the same name—if you have a local variable named --foo, var(--foo) will be substituted by that local variable, not by an argument or a custom property defined outside. The other values can still be accessed, however: setting the --foo local variable to initial will resolve it to the --foo parameter, while inherit will resolve it to the --foo custom property from the call site.
@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 */ }
Similarly, custom properties are implicitly available:
@function --double-z () returns <number>{ result : calc ( var ( --z) *2 ); } div{ --z : 3 ; z-index : --double-z (); /* 6 */ }
But function parameters "shadow" custom properties, and local variables "shadow" both:
@function --add-a-b-c ( --b, --c) { --c : 300 ; result : calc ( var ( --a) +var ( --b) +var ( --c)); /* uses the --a from the call site's custom property, the --b from the function parameter, and the --c from the local variable */ } div{ --a : 1 ; --b : 2 ; --c : 3 ; z-index : --add-a-b-c ( 20 , 30 ); /* 321 */ }
3. Using Custom Functions
Similar to how the value of a custom property can be substituted into the value of another property with var(), the result of a custom function evaluation can be substituted into the value of a property with a <dashed-function>.
A <dashed-function> is a functional notation whose function name starts with two dashes (U+002D HYPHEN-MINUS). Its argument grammar is:
<dashed-function> = --*( <declaration-value>#? )
A <dashed-function> can only be used where var() is allowed.
If a property contains one or more <dashed-function>s, the entire property’s grammar must be assumed to be valid at parse time. At computed-value time, every <dashed-function> must be replaced before finally being checked against the property’s grammar.
Note: Within the body of a custom function, var() functions might resolve differently than on the element the <dashed-function> is used on. See § 3.1 Evaluating Custom Functions.
A <dashed-function> is evaluated in some context: either in a property value on an element (or in a descriptor that is eventually treated like a property on an element, such as in @keyframes), or in a descriptor in the function body of another custom function that is being applied to a "hypothetical" element. Either way, this provides a calling context, which contains the property or descriptor name containing the <dashed-function>, and the element (or "hypothetical" element) that property/descriptor is being applied to.
As calling contexts are nested by <dashed-function> evaluations inside of custom functions, a calling context’s root element is the real element at the root of the calling context stack.
-
Let function be the result of dereferencing the dashed function’s name as a tree-scoped reference. If no such name exists, return the guaranteed-invalid value.
-
For each arg in arguments, substitute arbitrary substitution functions in arg, and replace arg with the result.
Note: This may leave some (or all) arguments as the guaranteed-invalid value, triggering default values (if any).
-
If dashed function is being substituted into a property on an element, let calling context be a calling context with that element and that property
Otherwise, it’s being substituted into a descriptor on a "hypothetical element", while evaluating another custom function. Let calling context be a calling context with that "hypothetical element" and that descriptor.
-
Evaluate a custom function, using function, arguments, and calling context, and return the equivalent token sequence of the value resulting from the evaluation.
{}
:
@function --max-plus-x ( --list, --x) { result : calc ( max ( var ( --list)) +var ( --x)); } div{ width : --max-plus-x ({ 1 px , 7 px , 2 px }, 3 px ); /* 10px */ }
--foo ()
is in a cycle with itself:
@function --foo ( --x) { result : --foo ( 10 ); }
Similarly,
is in a cycle with itself,
even though the local variable --x
is never referenced
by result:
@function --bar () { --x : --bar (); result : 1 ; }
However,
is not in a cycle with itself here,
since we never evaluate the result
declaration within
the
rule:
@function --baz ( --x) { @media ( unknown-feature) { result : --baz ( 42 ); } result:1 ; }
--baz ()
is not in a cycle in the example below:
even though var ( --x)
and var ( --y)
appear in the function body,
they refer to a function parameter and local variable, respectively.
The custom properties --x
and --y
both reference --baz ()
, but that’s fine:
those custom properties are not referenced within --baz ()
.
@function --baz ( --x) { --y : 10 px ; result : calc ( var ( --x) +var ( --y)); } div{ --x : --baz ( 1 px ); --y : --baz ( 2 px ); width : var ( --x); /* 11px */ height:var ( --y); /* 12px */ }
3.1. Evaluating Custom Functions
Custom functions are evaluated by, essentially, pretending their function body is a style rule being applied to a hypothetical element, resolving styles as normal, and then returning the value of the result descriptor on that hypothetical element. The hypothetical element "inherits" the values of all custom properties as if it were a child of its calling context, with its function parameters overriding "inherited" custom properties of the same name.
-
Let substitution context be a substitution context containing «"function", custom function».
Note: Due to tree-scoping, the same function name may appear multiple times on the stack while referring to different custom functions. For this reason, the custom function itself is included in the substitution context, not just its name.
-
Guard substitution context for the remainder of this algorithm. If substitution context is marked as cyclic, return the guaranteed-invalid value.
-
If the number of items in arguments is greater than the number of function parameters in custom function, return the guaranteed-invalid value.
-
Let registrations be an initially empty set of custom property registrations.
-
For each function parameter of custom function, create a custom property registration with the parameter’s name, a syntax of the parameter type, an inherit flag of "true", and no initial value. Add the registration to registrations.
-
If custom function has a return type, create a custom property registration with the name "return" (violating the usual rules for what a registration’s name can be), a syntax of the return type, an inherit flag of "false", and no initial value. Add the registration to registrations.
-
Let argument rule be an initially empty style rule.
-
For each function parameter of custom function:
-
Let arg value be the value of the corresponding argument in arguments, or the guaranteed-invalid value if there is no corresponding argument.
-
Let default value be the parameter’s default value.
-
Add a custom property to argument rule with a name of the parameter’s name, and a value of first-valid(arg value, default value).
-
-
Resolve function styles using custom function, argument styles, registrations, and calling context. Let argument styles be the result.
-
Let body rule be the function body of custom function, as a style rule.
-
For each custom property registration of registrations except the registration with the name "result", set its initial value to the corresponding value in argument styles, set its syntax to the universal syntax definition, and prepend a custom property to body rule with the property name and value in argument styles.
-
Resolve function styles using custom function, body rule, registrations, and calling context. Let body styles be the result.
-
If substitution context is marked as a cyclic substitution context, return the guaranteed-invalid value.
Note: Nested arbitrary substitution functions may have marked substitution context as cyclic at some point after step 2, for example when resolving result.
-
Return the value of the result property in body styles.
-
Create a "hypothetical element" el that acts as a child of calling context’s element. el is featureless, and only custom properties and the result descriptor apply to it.
-
Apply rule to el to the specified value stage, with the following changes:
-
Only the custom property registrations in registrations are visible; all other custom properties are treated as unregistered.
-
The inherited value of calling context’s property is the guaranteed-invalid value.
-
On custom properties, the CSS-wide keywords initial and inherit have their usual effect; all other CSS-wide keywords resolve to the guaranteed-invalid value.
Note: initial references the custom property registration created from the function parameters, letting you "reset" a property to the passed value. inherit inherits from the calling context’s element.\
On result, all CSS-wide keywords are left unresolved.
Note: result: inherit, for example, will cause the <dashed-function> to evaluate to the inherit keyword, similar to var(--unknown, inherit).
-
For a given custom property prop, during property replacement for that property, the substitution context also includes custom function. In other words, the substitution context is «"property", prop’s name, custom function»
Note: Due to dynamic scoping, the same property name may appear multiple times on the stack while referring to different custom properties. For this reason, the custom function itself is included in the substitution context, not just its name.
-
-
Determine the computed value of all custom properties and the result "property" on el, as defined in CSS Properties and Values API 1 § 2.4 Computed Value-Time Behavior, with changes from the previous step, and the following:
-
Aside from references to custom properties (which use the values on el as normal) and numbers/percentages (which are left unresolved in custom properties, as normal), all values which would normally refer to the element being styled instead refer to calling context’s root element.
Note: For example, attr() in a property, or @container queries in the rule.
-
-
Return el’s styles.
Note: Only custom properties and the result descriptor will be used from these styles.
4. Execution Model of Custom Functions
Like the rest of CSS, custom functions adhere to a declarative model.
The local variable descriptors and result descriptor can appear in any order, and may be provided multiple times. If this happens, then declarations appearing later win over earlier ones.
@function --mypi () { result : 3 ; result : 3.14 ; }
The value of the result descriptor of --mypi
is
.
@function --circle-area ( --r) { result : calc ( pi *var ( --r2)); --r2 : var ( --r) *var ( --r); }
Local variable descriptors may appear before or after they are referenced.
4.1. Conditional Rules
A conditional group rule that appears within a @function becomes a nested group rule, with the additional restriction that only descriptors allowed within @function are allowed within the nested group rule.
Conditional group rules within @function are processed as normal, acting as if the contents of the rule were present at the conditional group rule’s location when the condition is true, or acting as if nothing exists at that location otherwise.
@function --suitable-font-size () { result : 16 px ; @media ( width >1000 px ) { result : 20 px ; } }
The value of the result descriptor
is
if the media query’s condition is true,
and
otherwise.
@function --suitable-font-size () { @media ( width >1000 px ) { result : 20 px ; } result:16 px ; }
The value of the result descriptor
is always
in the above example.
@function --suitable-font-size () { --size : 16 px ; @media ( width >1000 px ) { --size : 20 px ; } result:var ( --size); }
5. Defining Mixins
A mixin is in many ways similar to a custom function, but rather than extending/upgrading custom properties, mixins extend/upgrade nested style rules, making them reusable and customizable with arguments.
@mixin --gradient-text ( --fromtype ( color) : mediumvioletred, --totype ( color) : teal, --angle: to bottom right, ) { color : env ( --from, env ( --to)); @supports ( background-clip: text) or( -webkit-background-clip: text) { @env --gradient:linear-gradient ( env ( --angle), env ( --from), env ( --to)); background : env ( --gradient, env ( --from)); color : transparent; -webkit-background-clip : text; background-clip : text; } } h1{ @apply --gradient-text ( pink, powderblue); }
Note that this example also uses a scoped environment variable (along with the arguments, which implicitly define scoped environment variables) which is scoped to the rule itself (rather than being applied to the element, like a custom property would be) to hold a temporary value to aid in readability of the mixin, without polluting the element’s styles with unwanted custom properties.
This is exactly equivalent to writing a nested style rule literally into the `h1` styles:
h1{ @nest { @env --from: pink; @env --to: powderblue; @env --angle: to bottom right; color : env ( --from, env ( --to)); @supports ( background-clip: text) or( -webkit-background-clip: text) { @env --gradient:linear-gradient ( env ( --angle), env ( --from), env ( --to)); background : env ( --gradient, env ( --from)); color : transparent; -webkit-background-clip : text; background-clip : text; } } }
(Where @nest is a fictitious rule representing a nested declarations rule.)
The entire @mixin feature is experimental and under active development, and is much less stable than @function. Expect things to change frequently for now.
5.1. The @mixin rule
The @mixin rule defines a mixin, and consists of a name, a list of mixin parameters, and a mixin body. (Identical to @function, save that it lacks a return type.)
<@mixin > =@mixin <function-token> <function-parameter>#?, @contents ?) { <declaration-rule-list>}
Differing from the @function rule, the final item in the parameters list can be the <at-keyword-token> @contents, indicating that this mixin accepts a @contents block.
If a default value and a parameter type are both provided, then the default value must parse successfully according to that parameter type’s syntax. Otherwise, the @mixin rule is invalid.
5.1.1. The Mixin Preamble
The <function-token> production must start with two dashes (U+002D HYPHEN-MINUS), similar to <dashed-ident>, or else the definition is invalid.
The name of the resulting mixin is given by the name of the <function-token>, the optional mixin parameters are given by the <function-parameter> values (defaulting to an empty set).
The name of a @mixin rule is a tree-scoped name. If more than one @mixin exists for a given name, then the rule in the stronger cascade layer wins, and rules defined later win within the same layer.
If the mixin parameters contain the same <custom-property-name> more than once, then the @mixin rule is invalid.
5.1.2. The Mixin Body
The body of a @mixin rule acts as a nested declarations rule, and accepts the same properties and rules that a normal nested declarations rule would.
In particular, further mixins can be invoked (via the @apply rule) within a mixin body.
Unknown properties and rules are invalid and ignored, but do not make the @mixin rule itself invalid.
5.2. Mixin Parameters
Within a mixin body, the env() function can access scoped environment variables defined within the mixin body, defined by the mixin’s arguments, or those defined at the call site (a style rule, or another mixin).
In that list, earlier things "win" over later things of the same name, exactly as if the mixin body was a nested declarations rule placed at its call site. Specifically, it desugars to two nested declarations rules, to correctly reproduce the argument scope.
@mixin --nested ( --color2: green) { @env --color3: blue; background : linear-gradient ( env ( --color1), env ( --color2), env ( --color3)); } p.nested{ @env --color1: red; @apply --nested (); }
is exactly equivalent to:
p.nested{ @env --color1: red; @nest { @env --color2: green; @nest { @env --color3: blue; background : linear-gradient ( env ( --color1), env ( --color2), env ( --color3)); } } }
(Where @nest here is a fictitous rule representing a nested declarations rule.)
@mixin --z-index-a-b-c ( --b, --c) { @env --c:300 ; z-index : calc ( env ( --a) +env ( --b) +env ( --c)); /* uses the --a from the call site's envs, the --b from the mixin parameter, and the --c from the local env */ } div{ @env --a:1 ; @env --b:2 ; @env --c:3 ; @apply --add-a-b-c ( 20 , 30 ); /* 321 */ }
Note that mixin parameters are scoped environment variables rather than custom properties, which means they exist in a separate namespace and don’t interfere with custom properties.
The two generally act very similarly, except for the slightly different definition syntax (a custom property versus an @env rule) and substitution functions (var() versus env()), but using custom properties actually writes those properties into the invoking style rule (potentially interacting with other definitions and uses of that custom property, which can be good or bad), while @env rules in a mixin aren’t visible to anything outside of the mixin body.
@mixin --tint-shade ( --hue <angle>) { --light-tint : lch ( 90 % 20 % env ( --hue)); --dark-shade : lch ( 10 % 80 % env ( --hue)); } .warning{ @apply --tint-shade ( 0 deg ); background : var ( --light-tint); color : var ( --dark-shade); padding : .5 em ; border : thick solid currentcolor; }
If the mixin had instead declared them as @env --light-tint: ...;/etc, then those would only be available within the mixin body, and the .warning rule would not be able to use them.
Note as well that scoped environment variables do not have an element context, so they can’t fully resolve values that require an element context, like em lengths or var() functions, at definition time. Instead, they’ll be resolved at each point they’re used, similar to an unregistered custom property.
@mixin --triple-border ( --size <length>) { &, :has ( &), & > *{ border-width : env ( --size); } } section{ font-size : 16 px ; } section > h1{ font-size : 30 px ; @apply --triple-border ( .5 em ); } section > h1 > small{ font-size : 20 px ; }
In this example, despite them all being set with the same env(--size) value, the `section` element has a border of 8px, the `h1` element has a border of 15px, and the `small` element has a border of 10px.
There are some ways to work around this, if desired. For example, the value can be stored in a registered custom property with the appropriate type, so it resolves on one element and then inherits as the absolute value, or passed through a custom function with a typed argument.
@function --as-length ( --arg <length>) { result : var ( --arg); } @mixin --triple-border ( --size <length>) { :has ( &) { --triple-border-size : --as-length ( env ( --size)); } &, :has ( &), & > *{ border-width : var ( --triple-border-size); } } section{ font-size : 16 px ; } section > h1{ font-size : 30 px ; @apply --triple-border ( .5 em ); } section > h1 > small{ font-size : 20 px ; }
In the above code, the mixin parameter is used to set a custom property on the parent element, where it’s forced to resolve as a length due to being passed through the custom function. All the styles then use that custom property (via var()) rather than the mixin parameter (via env()), so they all share the same already-resolved value.
(Note that this value is resolved on the parent element the custom property is defined on, becoming 8px, rather than resolving against the element the mixin is called on, which would give 15px. That’s an unavoidable limitation of this situation.)
Do we want to try and figure out a way to have a mixin parameter "remember" what element it was called on, so it can resolve against that element consistently, rather than require these workarounds? I think that’s inherently cyclic, tho—imagine an em length used to set font-size on a parent element.
5.3. The @contents Rule
In addition to accepting arguments passed by the <dashed-function> in the @apply rule, a mixin can accept a contents block. This is indicated by the mixin using a final @contents <at-keyword-token> in its parameter list, and is passed by giving the @apply rule invoking the mixin a block.
This allows the invoker of the mixin to pass an entire style block, which the mixin can then substitute into itself. This is useful, for example, if the mixin handles some common conditions for the author, and substitutes the contents block into a predefined @media or @container rule.
The syntax of a @contents at-rule is:
<@contents > =@contents [ { <declaration-list>} ] ?
That is, it is either an empty statement ended immediately by a semicolon, or a block treated as a nested declarations rule. The empty statement form behaves identically to passing an empty block.
If the mixin did not declare a @contents parameter, the @contents rule is ignored, substituting with nothing. Otherwise, if the @apply rule invoking the mixin passed a contents block, the @contents is replaced with the contents block, treating it as a nested declarations rule. Otherwise, if the @apply rule did not pass a contents block, the @contents rule is replaced with its own <declaration-list>, treated as a nested declarations rule.
Outside of a mixin body, the @contents rule is invalid and ignored.
@mixin --one-column ( @contents ) { @media ( width <=800 px ) { @contents ; } } @mixin --two-column ( @contents ) { @media ( width >800 px ) { @contents ; } } body{ @apply --one-column{ display : flex; flex-flow : column; } @apply --two-column{ display : grid; grid-template-columns : ; } }
6. Using Mixins
The result of a mixin application is substituted into the body of another style rule as a nested declarations rule via the @apply rule.
6.1. The @apply Rule
The @apply rule applies a mixin, causing it to substitute into the rule in place of the @apply rule itself.
Its grammar is:
<@apply > =@apply [ <dashed-ident> | <dashed-function>] [ { <declaration-list>} ] ?;
The @apply rule is only valid in the body of a style rule or nested group rule; using it in any other context causes it to be invalid and ignored.
@apply rules are processed before any styles are applied, as they effectively modify the stylesheet itself. (Similar, in effect, to how conditional group rules adjust which properties and rules are active in a stylesheet before styles are applied.)
The @apply rule applies the mixin named by the <dashed-ident> or the <dashed-function>’s name. If no such mixin exists, the @apply does nothing.
If passed a <dashed-function>, the arguments passed to the <dashed-function> are mapped to the mixin’s arguments; if more arguments are passed than the length of the mixin’s argument list, the @apply application does nothing. (Passing too few arguments is fine; the missing arguments take their default values instead.) A <dashed-ident> passes no arguments. (That is, @apply --foo; is identical to @apply --foo();.)
If the mixin declares a @contents parameter, and the @apply rule has a <declaration-list> block, that block is passed as its contents block. If the mixin did not declare a @contents parameter, having a <declaration-list> block makes the @apply rule invalid (similar to passing too many arguments).
7. Scoped Environment Variables
This section should move to [css-env-1] (or level 2, whatever).
The env() function, defined in [css-env-1], allows substituting the value of environment variables into a stylesheet. These are "global" variables, defined by the user agent, rather than custom properties defined by the page author on individual elements (and their descendants).
The @env rule allows defining scoped environment variables, which are lexically scoped to a single rule in a stylesheet (and any nested style rules within it).
A top-level @env probably needs to still be lexically scoped to just the stylesheet itself. (After all, you can use `media=""` to link in a stylesheet *effectively* auto-wrapped in an @media, and it would be weird to have that act differently from actually using a wrapping @media.) That means only JS-defined custom envs are available cross-stylesheet.
7.1. The @env Rule
The @env rule defines a scoped environment variable, scoped to its parent rule (and any other nested rule within its parent rule). It’s only valid within a nested style rule or nested group rule, or at the "top level" of a stylesheet not nested in anything; in any other context it’s invalid and ignored. Its grammar is:
<@env > =@env <custom-property-name> : <declaration-value>?;
This defines a scoped environment variable with a name given by the <custom-property-name>, and a value given by the <declaration-value>?. Its scope is the rule it’s nested in, or the stylesheet it’s defined in if it’s not nested.
7.1.1. Using Scoped Environment Variables
When an env() function is used with a <dashed-ident> as the name of the environment variable, it’s potentially accessing a scoped environment variable.
First the parent rule of the property or rule the env() is used in is checked to see if a scoped environment variable of that name exists scoped to that rule. If not, its parent rule is checked, recursively, until finally the stylesheet itself is checked. If that fails, the global environment variables are finally checked.
Note: Custom global environment variables can be defined by the CSS.customEnv
API.
(To be defined.)
Need some analogue to inherit, but for the parent lexical scopes. Probably can’t use inherit itself, as that’s a meaningful value that an env() could resolve to, I guess?
7.1.2. Element-Dependent Values
A scoped environment variable can be defined (both explicitly, and implicitly via mixin parameters) that require knowledge about the element the style is being applied to, but env() can be used in locations that don’t have an element context.
@mixin --smaller ( --size <length>:800 px , @contents ) { @media ( width <=env ( --size)) { @contents ; } } h1{ @apply --smaller{ margin : 1 em 0 ; } }
When this situation occurs, if the value of the env() can be evaluated in its subsitution context at all, it evaluates according to the normal rules for that context. (For example, in a @media, em values resolve against the initial font size, rather than the font size of any particular element.)
If the value would be invalid in its substitution context, it’s instead replaced with the guaranteed-invalid value. (For example, var() isn’t allowed in a @media.)
8. CSSOM
8.1. The CSSFunctionRule
Interface
The CSSFunctionRule
interface represents a @function rule.
[Exposed =Window ]interface :
CSSFunctionRule CSSGroupingRule {readonly attribute CSSOMString name ;sequence <FunctionParameter >();
getParameters readonly attribute CSSOMString returnType ; };
name
, of type CSSOMString, readonly- The name of the custom function.
returnType
, of type CSSOMString, readonly-
The return type of the custom function,
represented as a syntax string.
If the custom function has no return type,
returns
."*"
dictionary {
FunctionParameter required CSSOMString ;
name required CSSOMString ;
type CSSOMString ?; };
defaultValue
- name
- The name of the function parameter.
- type
-
The type of the function parameter,
represented as a syntax string,
or
if the parameter has no type."*" - defaultValue
- The default value of the function parameter, or `null` if the argument does not have a default.
While declarations may be specified directly within a @function rule,
they are not represented as such in the CSSOM.
Instead, consecutive segments of declarations
appear as if wrapped in CSSFunctionDeclarations
rules.
Note: This also applies to the "leading" declarations in the @function rule, i.e those that do not follow another nested rule.
@function --bar () { --x : 42 ; result : var ( --y); @media ( width >1000 px ) { /* ... */ } --y:var ( --x); }
The above will appear in the CSSOM as:
@function --bar () { /* CSSFunctionDeclarations { */ --x:42 ; result : var ( --y); /* } */ @media ( width >1000 px ) { /* ... */ } /* CSSFunctionDeclarations { */ --y:var ( --x); /* } */ }
-
The string
followed by a single SPACE (U+0020)."@function" -
The result of performing serialize an identifier on the name of the custom function, followed by a single LEFT PARENTHESIS (U+0028).
-
The result of serialize a function parameter on each of the custom function’s parameters, all joined by
(COMMA U+002C, followed by a single SPACE U+0020).", " -
A single RIGHT PARENTHESIS (U+0029).
-
If the custom function has return type, and that return type is not the universal syntax definition ("*"):
-
A single SPACE (U+0020), followed by the string
, followed by a single SPACE (U+0020)."returns" -
The result of performing serialize a CSS type on that type, followed by a single SPACE (U+0020).
-
-
A single LEFT CURLY BRACKET (U+007B), followed by a SPACE (U+0020).
-
The result of performing serialize a CSS rule on each rule in cssRules, filtering out empty strings, all joined by a single SPACE (U+0020).
Note: Serialize a CSS rule can return an empty string when serializing an empty
CSSFunctionDeclarations
rule. -
A single SPACE (U+0020), followed by a single RIGHT CURLY BRACKET (U+007D).
-
The result of performing serialize an identifier on the name of the function parameter.
-
If the function parameter has a type, and that type is not the universal syntax definition:
-
A single SPACE (U+0020), followed by the result of performing serialize a CSS type on that type.
-
-
If the function parameter has a default value:
-
A single COLON (U+003A), followed by a single SPACE (U+0020), followed by the result of performing serialize a CSS value on that value.
-
-
If the <css-type> consists of a single <syntax-component>, return the corresponding syntax string.
-
Otherwise, return the concatenation of the following:
-
The string
, i.e."type("
followed by a single LEFT PARENTHESIS (U+0028)."type" -
The corresponding syntax string.
-
The string
, i.e. a single RIGHT PARENTHESIS (U+0029).")"
-
8.2. The CSSFunctionDeclarations
Interface
The CSSFunctionDeclarations
interface represents a run
of consecutive declarations within a @function rule.
[Exposed =Window ]interface :
CSSFunctionDescriptors CSSStyleDeclaration {attribute [LegacyNullToEmptyString ]CSSOMString ; }; [
result Exposed =Window ]interface :
CSSFunctionDeclarations CSSRule { [SameObject ,PutForwards =cssText ]readonly attribute CSSFunctionDescriptors style ; };
style
attribute
must return a CSSFunctionDescriptors
object for the rule,
with the following properties:
- computed flag
-
Unset
- readonly flag
-
Unset
- declarations
-
The declared declarations in the rule, in specified order. This includes any local variables.
- parent CSS rule
- owner node
-
Null
The CSSFunctionDeclarations
rule, like CSSNestedDeclarations
,
serializes as if its declaration block
had been serialized directly.
9. Privacy Considerations
The constructs defined by this specification are defined and used entirely within CSS; they expose no new information.
10. Security Considerations
No issues have been opened against this specification.