Title:  CSS Properties and Values API Level 1
Status: ED
Group: houdini
ED: https://drafts.css-houdini.org/css-properties-values-api-1/
Previous Version: http://www.w3.org/TR/2016/WD-css-properties-values-api-1-20160607/
Shortname: css-properties-values-api
Level: 1
Abstract: This CSS module defines an API for registering new CSS properties. Properties registered using this API are provided with a parse syntax that defines a type, inheritance behaviour, and an initial value.
Editor: Tab Atkins, jackalmage@gmail.com
Editor: Shane Stephens, shanestephens@google.com
Editor: Daniel Glazman, daniel.glazman@disruptive-innovations.com
Editor: Alan Stearns, stearns@adobe.com
Editor: Elliot Sprehn, esprehn@chromium.org
Editor: Greg Whitworth, gwhit@microsoft.com
Ignored Terms: boolean, Animatable, Map, Context, isolated worker, SyntaxError,
Ignored Terms: InvalidModificationError, NotFoundError, StylePropertyMapReadOnly,
Ignored Terms: worklet global scope
Ignored Terms: throw, NotSupportedError, isconstructor, get, iscallable,
Ignored Terms: construct, name map of inputs
Ignored Vars: arguments, methodPropertyKey, inputStyleMap, workletGlobalScope
Ignored Terms: WorkletGlobalContext
Repository: w3c/css-houdini-drafts
{
	"css-paint-api": {
		"title": "CSS Painting API"
	},
	"css-layout-api": {
		"title": "CSS Layout API"
	}
}
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} ==============================================================
dictionary PropertyDescriptor {
	required DOMString name;
	         DOMString syntax       = "*";
	         boolean   inherits     = false;
	         DOMString initialValue;
};

partial interface CSS {
	static void registerProperty(PropertyDescriptor descriptor);
	static void unregisterProperty(DOMString name);
};
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:
	.thing {
		--my-color: green;
		--my-color: url("not-a-color");
		color: var(--my-color);
	}
	
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:
	CSS.registerProperty({
		name: "--my-color",
		syntax: "<color>",
		initialValue: "black"
	});
	
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 <