Skip to content

[css-properties-values-api] A compact syntax for registering global constants/custom properties #9206

@mirisuzanne

Description

@mirisuzanne

There has been discussion in several issues recently about providing a ::document selector, or @document/@global/@env rule that would allow authors a quick way to register globally available constants (as custom properties or environment variables) - without a full @property rule describing each individually. See, for example:

(Hopefully this issue is useful as a way of combining several discussions, and not just a duplicate)

Those first two issues have more specific concerns that could be (or have been) addressed in specific ways - but mention the possibility we might still want a more generic solution for registering global properties. Custom media queries (@custom-media) have similarly been solved as a specific case, but not yet implemented – and there's an open issue for adding @custom-container. Those use-cases could also be solved by a well-defined global parameter registry.

There are two primary overlapping concerns here:

  • A less verbose syntax for authors to register custom properties with an initial value and syntax. As @LeaVerou has pointed out, authors define a lot of variables, and the @property rule is a very bulky way to register them one-at-a-time.
  • Authors would like to define global values that can be reused in at-rules, or other places where the cascading var() would not be possible to resolve.

I tend to agree with @tabatkins that:

Having an author-defined env() might work, but would require an author to duplicate values across properties and env(), or else decide for each whether they want to express it as a var or an env().

I like the proposed solution of allowing global reference to the initial values of registered custom properties, but think we need to make that registration simpler for it to work. I don't know the right syntax for that, but if we're able to come up with something compact, it could also be useful for defining parameters in declarative custom functions and (someday, maybe) mixins.

The @property rule can register a custom property name with three associated descriptors:

  • syntax: the CSS type/grammar(s) used to validate and interpolate values (often but not always a single type like "<color>" or "<length>"'
  • inherits: a boolean to set if the property inherits or not (this would not be needed in function/mixin parameters)
  • initial-value: the initial value of the property

Authors often skip this registration for most properties, and only define the name along with an un-registered not-technically-'initial' value on the root element. Then some authors add the @property registration for specific properties - often to define a syntax for the sake of interpolation, but occasionally to set inheritance or a more formal initial value.

Things I would expect from a syntax:

  • Optionally register just the name and initial value (default to universal syntax, with inheritance on)
  • Optionally register props with the guaranteed invalid initial value
  • Optionally register groups of properties that share a syntax

The main complexity that I see with defining a compact syntax is that custom property values (including initial values) are very permissive. It will be difficult to combine initial-value with anything else on the right side of a standard property:value; format – though it should be possible using either the ! delim-token, or wrapping ()/{}/[] of some kind. The other thought I had was to put some of the definition on the left side. Some rough ideas:

@global {
  --my-color("<color>"): mediumvioletred;
  --my-padding("<length>"): 10px !no-inherit;
}

@global "<color>" no-inherit {
  --brand-color: teal;
}

The an all-in-one syntax is more appropriate for reuse in function parameter definitions, while the grouped syntax helps with defining a whole set of related variables in a single place.

On the other end, when calling custom properties, a env() or global() function could give access to the initial registered value anywhere in the document, while var() returns only the cascaded value. I prefer this to a context-dependent resolution of var(), since it provides more clarity on what value to expect, and makes both available where var() is already supported:

.my-thing {
  color: var(--brand-color);
  padding: global(--my-padding);

  @media global(--above-small) {
    ...
  }
}

This is far from a complete proposal, and mostly a request that we consider these use-cases together. Happy for thoughts, and willing to merge this into the env() conversation if it belongs there.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions