stylelint: Add `no-invalid-position-var-function`

What is the problem you’re trying to solve?

Using the var() function inside a @ rule block is a CSS syntax error that should be detectable:

@font-face {
  font-family: foo;
  font-weight: var(--font-weight-normal); /* Syntax Error */
}

Firefox will raise a Unknown descriptor ‘var(’ in @font-face rule. Skipped to next declaration..

What solution would you like to see?

A rule that detects such cases. I dug through the CSS specs and found:

Block at-rules will typically contain a collection of (generic or at-rule–specific) at-rules, qualified rules, and/or descriptor declarations subject to limitations defined by the at-rule. Descriptors are similar to properties (and are declared with the same syntax) but are associated with a particular type of at-rule rather than with elements and boxes in the tree.

The var() function can be used in place of any part of a value in any property on an element. The var() function can not be used as property names, selectors, or anything else besides property values. (Doing so usually produces invalid syntax, or else a value whose meaning has no connection to the variable.)

I’m sure there are more functions besides var that would be invalid for a descriptor declaration, but a rule that just warns for var would be a good start.

About this issue

  • Original URL
  • State: open
  • Created a year ago
  • Comments: 22 (16 by maintainers)

Most upvoted comments

Part of the issue here is that var() is expected by many to just work everywhere.

That’s a good summary.

I think this is also easier to test and verify so that it is correct with the CSS specification.

I agree.

So, how about function-var-no-invalid-location?

SGTM. It’ll tie into the “The var() function can not be used as property names, selectors, or anything else besides property values” part of the spec that @silverwind mentioned above.

I had thought env and var were similar and so a rule would apply to them both, but it turns out env can be used in more places:

In addition, unlike custom properties, which cannot be used outside of declarations, the env() function can be used in place of any part of a property value, or any part of a descriptor (e.g. in Media query rules). As the spec evolves, it may also be usable in other places such as selectors.

Our closest existing rule is no-invalid-position-at-import-rule, so called because it’s scoped to the whole source like this rule.

Suggested blueprint:

  • Name: no-invalid-position-var-function
  • Description: Disallow invalid position var() functions.
  • Message: Unexpected invalid position var() function
  • Primary option: true
  • Secondary options: n/a
  • Category: Avoid errors > Invalid
  • Included in sharable configs?: Yes, recommended.
  • Expanded description:

The var() function can be used in place of any part of a value in any property on an element. The var() function can not be used as property names, selectors, or anything else besides property values.

This rule checks:

  • at-rule descriptor names and values
  • media queries
  • property names
  • selectors

We’ll want to ensure we don’t needlessly call any of the constructor parsers when checking any of the above.

According to the CSS tree reference, the following at-rule have descriptors:

  • counter-style
  • font-face
  • page
  • property
  • viewport

Shall I label as ready to implement?

Maybe it can be approached from a different perspective?

Part of the issue here is that var() is expected by many to just work everywhere. Especially for people coming from preprocessors like scss.

A rule that warns on invalid locations of var() functions could cover :

  • used in @font-face descriptor values
  • used in @media feature values
  • used in selectors

I think this is also easier to test and verify so that it is correct with the CSS specification.

If I recall correctly this is the difference between descriptors and properties. @font-face, @property, @counter-style all take a list of descriptors, whereas @page takes a list of declarations (property / value pairs).

https://www.w3.org/TR/css-variables-1/#using-variables

The value of a custom property can be substituted into the value of another property with the var() function. The syntax of var() is:

https://www.w3.org/TR/css-fonts-4/#font-face-rule

Edit :

@page also has a set of descriptors:

  • size
  • page-orientation

Okay, I suggest a blueprint:

  • Name: at-rule-no-invalid-descriptor
  • Description: Disallow invalid descriptors within at-rules.
  • Message: Unexpected invalid descriptor within "@<at-rule>"
  • Primary option: true
  • Secondary options: n/a
  • Category: Avoid errors > Invalid
  • Included in sharable configs?: No. But if it matured, we would include it in the recommended config.

Example:

@font-face {
  font-weight: var(--font-weight-normal);
/*             ^~~~~~~~~~~~~~~~~~~~~~~~~
               Unexpected invalid descriptor within "@font-face"
 */
}
{
  "at-rule-no-invalid-descriptor": true
}

Please feel free to give me any feedback.

Agree, renamed title to at-rule-no-invalid-descriptor.

If this rule is limited to at-rule, starting the rule name with at-rule- might be better. 🤔

Ref:

Agree, syntax errors are topics for a parser, not a linter.

@silverwind yeah, I think it is more about grammar parsing than linting, so I hope in future we resolve problems with our parser and will catch this on the parser side (or will have rule to apply grammar parsing to each things in your CSS)