Introduction {#intro}
=====================
CSS defines a comprehensive set of properties that can be manipulated in order
to modify the layout, paint, or behaviour of a web document. However, web authors
frequently wish to extend this set with additional properties.
[[css-variables]] provides primitive means for defining user-controlled properties,
however these properties always take token lists as values, must always inherit, and
can only impact document layout or paint by being re-incorporated into the value
of other properties via a var() reference.
This specification extends [[css-variables]], allowing the registration of properties
that have a value type, an initial value, and a defined inheritance behaviour.
This specification is complementary to [[css-paint-api]] and [[css-layout-api]], which
allow custom properties to directly impact paint and layout behaviours respectively.
Registering custom properties {#registering-custom-properties}
==============================================================
Additional, the {{Window}} object gains a new \[[registeredPropertySet]] private slot,
which is a set of records that describe registered custom properties.
The {{PropertyDescriptor}} dictionary {#the-propertydescriptor-dictionary}
--------------------------------------------------------------------------
A PropertyDescriptor dictionary represents author-specified configuration
options for a custom property. {{PropertyDescriptor}} dictionaries contain the
following members:
: name
:: The name of the custom property being defined.
: syntax
:: A string representing how this custom property is parsed.
: inherits
:: True if this custom property should inherit down the DOM tree; False otherwise.
: initialValue
:: The initial value of this custom property.
The {{registerProperty()}} and {{unregisterProperty()}} functions {#the-registerproperty-function}
--------------------------------------------------------------------------------------------------
The registerProperty(PropertyDescriptor descriptor) method
registers a custom property according to the configuration options provided in
descriptor.
When it is called,
it executes the register a custom property algorithm,
passing the options in its descriptor argument
as arguments of the same names.
To register a custom property
with |name| being a string,
and optionally
|syntax| being a string,
|inherits| being a boolean,
and |initialValue| being a string,
execute these steps:
1. Attempt to parse |name|
as a <>.
If this fails,
throw a {{SyntaxError}}
and exit this algorithm.
Otherwise,
let |parsed name| be the parsed value.
If the window's {{[[registeredPropertySet]]}} slot
already contains an entry with |parsed name| as its property name
(compared codepoint-wise),
throw an {{InvalidModificationError}}
and exit this algorithm.
2. If |syntax| is not present,
or is equal to "*" (U+002A ASTERISK),
let |parsed syntax| be undefined,
and skip to the next step of this algorithm.
Otherwise, attempt to parse |syntax|
according to the rules in [[#supported-syntax-strings]].
If it does not parse successfully,
throw a {{SyntaxError}}.
Otherwise,
let |parsed syntax| be the parsed syntax.
Note: For example, a valid syntax string is something like "<length>",
or "<number>+";
the allowed syntax is a subset of [[css-values-3#value-defs]].
Future levels of this specification are expected to expand the complexity of allowed syntax strings,
allowing custom properties that more closely resemble the full breadth of what CSS properties allow.
3. If |parsed syntax| is undefined,
and |initialValue| is not present,
let |parsed initial value| be empty.
This must be treated identically to the "default" initial value of custom properties,
as defined in [[!css-variables]].
Skip to the next step of this algorithm.
Otherwise,
if |parsed syntax| is undefined,
parse |initialValue| as a <>.
If this fails,
throw a {{SyntaxError}}
and exit this algorithm.
Otherwise,
let |parsed initial value| be the parsed result.
Skip to the next step of this algorithm.
Otherwise, if |initialValue| is not present,
throw a {{SyntaxError}}
and exit this algorithm.
Otherwise,
parse {{PropertyDescriptor/initialValue}}
according to |parsed syntax|.
If this fails,
throw a {{SyntaxError}}
and exit this algorithm.
Otherwise, let |parsed initial value| be the parsed result.
If |parsed initial value| is not computationally idempotent,
throw a {{SyntaxError}}
and exit this algorithm.
4. If |inherits| is present,
set |inherit flag| to its value.
Otherwise, set |inherit flag| to false.
5. Let |registered property| be a record
with a property name of |parsed name|,
a syntax of |parsed syntax|,
an initial value of |parsed initial value|,
and an inherit flag of |inherit flag|.
Add |registered property|
to the window's {{[[registeredPropertySet]]}}.
A property value is computationally idempotent
if it can be converted into a computed value
using only the value of the property on the element,
and "global" information that cannot be changed by CSS.
For example, ''5px'' is computationally idempotent,
as converting it into a computed value doesn't change it at all.
Similarly, ''1in'' is computationally idempotent,
as converting it into a computed value
relies only on the "global knowledge" that ''1in'' is ''96px'',
which can't be altered or adjusted by anything in CSS.
On the other hand, ''3em'' is not computationally idempotent,
because it relies on the value of 'font-size' on the element
(or the element's parent).
Neither is a value with a ''var()'' function,
because it relies on the value of a custom property.
When a custom property is registered with a given type,
the process via which specified values for that property are turned into computed values
is defined fully by the type selected,
as described in [[#calculation-of-computed-values]].
Properties can be unregistered using
unregisterProperty(DOMString name).
When it is called,
it executes the unregister a custom property algorithm,
with a name set to its sole argument.
To unregister a custom property with the name |name|:
1. If the window's {{[[registeredPropertySet]]}}
contains a record with a property name matching |name|
(compared codepoint-wise),
remove the record from the set.
Otherwise,
throw a {{NotFoundError}}.
When the window's {{[[registeredPropertySet]]}} changes,
previously syntactically invalid property values can become valid and vice versa.
This can change the set of declared values which requires the cascade to be recomputed.
By default, all custom property declarations that can be parsed as a sequence of tokens
are valid. Hence, the result of this stylesheet:
is to set the 'color' property of elements of class "thing" to ''inherit''.
The second '--my-color' declaration overrides the first at parse time (both are valid),
and the ''var()'' reference in the 'color' property is found to be invalid at computed-value time
(because ''url("not-a-color")'' is not a color).
At computed-value time the only available fallback is the default value,
which in the case of color is ''inherit''.
if we call:
then the second '--my-color' declaration becomes syntactically invalid at parse time,
and is ignored.
The first '--my-color' is the only valid declaration left for the property,
so 'color' is set to the value ''green''.
Supported syntax strings {#supported-syntax-strings}
----------------------------------------------------
The following syntax strings are supported:
: "<length>"
:: Any valid <> value
: "<number>"
:: <> values
: "<percentage>"
:: Any valid <> value
: "<length-percentage>"
:: Any valid <> or <> value, any valid <>
expression combining <> and <> components.
: "<color>"
:: Any valid <> value
: "<image>"
:: Any valid <> value
: "<url>"
:: Any valid <> value
: "<integer>"
:: Any valid <> value
: "<angle>"
:: Any valid <> value
: "<time>"
:: Any valid <