From c6e8c326635a069dd644e516b6cdf823fb2ca33c Mon Sep 17 00:00:00 2001 From: Keith Cirkel Date: Sun, 4 Feb 2024 23:14:13 +0000 Subject: [PATCH] update docs to reflect new css custom state syntax (#31258) * update docs to reflect new css custom state syntax * fix typos * fix top level description of add * Update files/en-us/web/api/customstateset/add/index.md * Update files/en-us/web/api/customstateset/index.md * Apply suggestions from code review * Update files/en-us/web/api/customstateset/index.md * Update files/en-us/web/api/customstateset/index.md * Update files/en-us/web/api/customstateset/index.md Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update files/en-us/web/api/customstateset/index.md * Fix bad selector Co-authored-by: Hamish Willee * remove mention of sytnaxerrors, as these no longer occur * remove mention of * remove mention of * Update files/en-us/web/api/customstateset/add/index.md * Update files/en-us/web/api/customstateset/index.md --------- Co-authored-by: Hamish Willee Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../en-us/web/api/customstateset/add/index.md | 15 +-- files/en-us/web/api/customstateset/index.md | 127 +++++++++++++----- .../using_custom_elements/index.md | 16 +-- 3 files changed, 108 insertions(+), 50 deletions(-) diff --git a/files/en-us/web/api/customstateset/add/index.md b/files/en-us/web/api/customstateset/add/index.md index c998be108a7edb0..e098a59079da930 100644 --- a/files/en-us/web/api/customstateset/add/index.md +++ b/files/en-us/web/api/customstateset/add/index.md @@ -10,7 +10,9 @@ browser-compat: api.CustomStateSet.add {{APIRef("Web Components")}}{{SeeCompatTable}} -The **`add`** method of the {{domxref("CustomStateSet")}} interface adds an item to the `CustomStateSet`, after checking that the value is in the correct format. +The **`add`** method of the {{domxref("CustomStateSet")}} interface adds value representing a custom state to the `CustomStateSet`. + +Custom elements with a specific state can be selected using the [`:state()`](/en-US/docs/Web/CSS/:state) pseudo-class, specifying the desired state as an argument. ## Syntax @@ -21,26 +23,21 @@ add(value) ### Parameters - `value` - - : A string which must be a ``, with the form `--mystate`. + - : A string that represents the custom state. ### Return value Undefined. -### Exceptions - -- `SyntaxError` {{domxref("DOMException")}} - - : Thrown if the string is not a ``. - ## Examples -The following function adds the state `--checked` to a `CustomStateSet`. +The following function adds the state `checked` to a `CustomStateSet`. ```js class MyCustomElement extends HTMLElement { set checked(flag) { if (flag) { - this._internals.states.add("--checked"); + this._internals.states.add("checked"); } } } diff --git a/files/en-us/web/api/customstateset/index.md b/files/en-us/web/api/customstateset/index.md index 1ecf621da7769a7..e1b05617546393c 100644 --- a/files/en-us/web/api/customstateset/index.md +++ b/files/en-us/web/api/customstateset/index.md @@ -21,7 +21,7 @@ The interface can be used to expose the internal states of a custom element, all ## Instance methods - {{domxref("CustomStateSet.add()")}} {{Experimental_Inline}} - - : Adds a value to the set, first checking that the _value_ is a ``. + - : Adds a value to the set. - {{domxref("CustomStateSet.clear()")}} {{Experimental_Inline}} - : Removes all elements from the `CustomStateSet` object. - {{domxref("CustomStateSet.delete()")}} {{Experimental_Inline}} @@ -53,7 +53,7 @@ To make the {{domxref("CustomStateSet")}} available, a custom element must first Note that `ElementInternals` cannot be attached to a custom element based on a built-in element, so this feature only works for autonomous custom elements (see [github.com/whatwg/html/issues/5166](https://github.com/whatwg/html/issues/5166)). The `CustomStateSet` instance is a [`Set`-like object](/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set#set-like_browser_apis) that can hold an ordered set of state values. -Each value is a dashed identifier, with the format: `--mystatename`. +Each value is a custom identifier. Identifiers can be added to the set or deleted. If an identifier is present in the set the particular state is `true`, while if it is removed the state is `false`. @@ -64,23 +64,26 @@ The states can be used within the custom element but are not directly accessible ### Interaction with CSS Developers can select a custom element with a specific state using its state _custom state pseudo-class_. -The format of this pseudo-class is `:--mystatename`, where `--mystatename` is the state as defined in the element. +The format of this pseudo-class is `:state(mystatename)`, where `mystatename` is the state as defined in the element. -The custom state pseudo-class matches the custom element only if the state is `true` (i.e. if `--mystatename` is present in the `CustomStateSet`). +The custom state pseudo-class matches the custom element only if the state is `true` (i.e. if `mystatename` is present in the `CustomStateSet`). + +> **Warning:** Chrome supports a deprecated syntax that selects custom states using a CSS `` rather than the `:state()` function. +> For information about how to support both approaches see the [Compatibility with `` syntax](compability_with_dashed-ident_syntax) section below. ## Examples ### Labeled Checkbox This example, which is adapted from the specification, demonstrates a custom checkbox element that has an internal "checked" state. -This is mapped to the `--checked` custom state, allowing styling to be applied using the `:--checked` custom state pseudo class. +This is mapped to the `checked` custom state, allowing styling to be applied using the `:state(checked)` custom state pseudo class. #### JavaScript First we define our class `LabeledCheckbox` which extends from `HTMLElement`. In the constructor we call the `super()` method, leaving most of the "work" to `connectedCallback()`, which is invoked when a custom element is added to the page. The content of the element is defined using a ` Label`; } get checked() { - return this._internals.states.has("--checked"); + return this._internals.states.has("checked"); } set checked(flag) { if (flag) { - this._internals.states.add("--checked"); + this._internals.states.add("checked"); } else { - this._internals.states.delete("--checked"); + this._internals.states.delete("checked"); } } @@ -132,8 +135,8 @@ In the `LabeledCheckbox` class: - The `connectedCallback()` method uses {{domxref("HTMLElement.attachInternals()", "`this.attachInternals()`")}} to attach an {{domxref("ElementInternals", "`ElementInternals`")}} object. - In the `get checked()` and `set checked()` we use `ElementInternals.states` to get the `CustomStateSet`. -- The `set checked(flag)` method adds the `"--checked"` dashed identifier to the `CustomStateSet` if the flag is set and delete the identifier if the flag is `false`. -- The `get checked()` method just checks whether the `--checked` property is defined in the set. +- The `set checked(flag)` method adds the `"checked"` identifier to the `CustomStateSet` if the flag is set and delete the identifier if the flag is `false`. +- The `get checked()` method just checks whether the `checked` property is defined in the set. - The property value is toggled when the element is clicked. We then call the {{domxref("CustomElementRegistry/define", "define()")}} method on the object returned by {{domxref("Window.customElements")}} in order to register the custom element: @@ -152,13 +155,13 @@ After registering the custom element we can use the element in HTML as shown: #### CSS -Finally we use the `:--checked` custom state pseudo class to select CSS for when the box is checked. +Finally we use the `:state(checked)` custom state pseudo class to select CSS for when the box is checked. ```css labeled-checkbox { border: dashed red; } -labeled-checkbox:--checked { +labeled-checkbox:state(checked) { border: solid; } ``` @@ -174,8 +177,8 @@ Click the element to see a different border being applied as the checkbox `check This example shows how to handle the case where the custom element has an internal property with multiple possible value. The custom element in this case has a `state` property with allowed values: "loading", "interactive" and "complete". -To make this work, we map each value to its custom state and create code to ensure that only the dashed identifier corresponding to the internal state is set. -You can see this in the implementation of the `set state()` method: we set the internal state, add the dashed identifier for the matching custom state to `CustomStateSet`, and remove the dashed identifiers associated with all the other values. +To make this work, we map each value to its custom state and create code to ensure that only the identifier corresponding to the internal state is set. +You can see this in the implementation of the `set state()` method: we set the internal state, add the identifier for the matching custom state to `CustomStateSet`, and remove the identifiers associated with all the other values. Most of the remaining code is similar to the example that demonstrates a single boolean state (we show different text for each state as the user toggles through them). @@ -200,9 +203,9 @@ class ManyStateElement extends HTMLElement { font-family: monospace; } :host::before { content: '[ unknown ]'; white-space: pre; } - :host(:--loading)::before { content: '[ loading ]' } - :host(:--interactive)::before { content: '[ interactive ]' } - :host(:--complete)::before { content: '[ complete ]' } + :host(:state(loading))::before { content: '[ loading ]' } + :host(:state(interactive))::before { content: '[ interactive ]' } + :host(:state(complete))::before { content: '[ complete ]' } Click me`; } @@ -213,22 +216,22 @@ class ManyStateElement extends HTMLElement { set state(stateName) { // Set internal state to passed value - // Add dashed identifier matching state and delete others + // Add identifier matching state and delete others if (stateName == "loading") { this._state = "loading"; - this._internals.states.add("--loading"); - this._internals.states.delete("--interactive"); - this._internals.states.delete("--complete"); + this._internals.states.add("loading"); + this._internals.states.delete("interactive"); + this._internals.states.delete("complete"); } else if (stateName == "interactive") { this._state = "interactive"; - this._internals.states.delete("--loading"); - this._internals.states.add("--interactive"); - this._internals.states.delete("--complete"); + this._internals.states.delete("loading"); + this._internals.states.add("interactive"); + this._internals.states.delete("complete"); } else if (stateName == "complete") { this._state = "complete"; - this._internals.states.delete("--loading"); - this._internals.states.delete("--interactive"); - this._internals.states.add("--complete"); + this._internals.states.delete("loading"); + this._internals.states.delete("interactive"); + this._internals.states.add("complete"); } } @@ -258,17 +261,17 @@ This is similar to the example that demonstrates a single boolean state, except #### CSS -In the CSS we use the three custom state pseudo classes to select CSS for each of the internal state values: `:--loading`, `:--interactive`, `:--complete`. +In the CSS we use the three custom state pseudo classes to select CSS for each of the internal state values: `:state(loading)`, `:state(interactive)`, `:state(complete)`. Note that the custom element code ensures that only one of these custom states can be defined at a time. ```css -many-state-element:--loading { +many-state-element:state(loading) { border: dotted grey; } -many-state-element:--interactive { +many-state-element:state(interactive) { border: dashed blue; } -many-state-element:--complete { +many-state-element:state(complete) { border: solid green; } ``` @@ -279,6 +282,64 @@ Click the element to see a different border being applied as the state changes. {{EmbedLiveSample("Non-boolean internal states", "100%", 50)}} +## Compability with `` syntax + +Previously custom elements with custom states were selected using a `` instead of the [`:state()`](/en-US/docs/Web/CSS/:state) function. +Browsers that don't support `:state()`, including versions of Chrome, will throw an error when supplied with an ident that is not prefixed with the double dash. +If support for these browsers is required, it is possible to use a `` as the state's value, and select it with both the `:--mystate` and `:state(--mystate)` CSS selector: + +### Using double dash prefixed idents + +#### JavaScript + +```js +class CompatibleStateElement extends HTMLElement { + connectedCallback() { + const internals = this.attachInternals(); + // The double dash is required in browsers with the + // legacy syntax, but works with the modern syntax + internals.states.set("--loaded"); + } +} +``` + +#### CSS + +```css +compatible-state-element:is(:--loaded, :state(--loaded)) { + border: solid green; +} +``` + +### Using a try catch block + +An alternative solution can be to use a `try` `catch` block to fall back to the legacy syntax: + +#### JavaScript + +```js +class CompatibleStateElement extends HTMLElement { + connectedCallback() { + const internals = this.attachInternals(); + // The double dash is required in browsers with the + // legacy syntax, not supplying it will throw + try { + internals.states.set("loaded"); + } catch { + internals.states.set("--loaded"); + } + } +} +``` + +#### CSS + +```css +compatible-state-element:is(:--loaded, :state(loaded)) { + border: solid green; +} +``` + ## Specifications {{Specifications}} diff --git a/files/en-us/web/api/web_components/using_custom_elements/index.md b/files/en-us/web/api/web_components/using_custom_elements/index.md index f9dad4877d2a14c..3e4dd580b836c86 100644 --- a/files/en-us/web/api/web_components/using_custom_elements/index.md +++ b/files/en-us/web/api/web_components/using_custom_elements/index.md @@ -186,8 +186,8 @@ The code below shows how this works using the example of an autonomous custom el The `collapsed` state is represented as a boolean property (with setter and getter methods) that is not visible outside of the element. To make this state selectable in CSS the custom element first calls {{domxref("HTMLElement.attachInternals()")}} in its constructor in order to attach an {{domxref("ElementInternals")}} object, which in turn provides access to a {{domxref("CustomStateSet")}} through the {{domxref("ElementInternals.states")}} property. -The setter for the (internal) collapsed state adds the _dashed identifier_ `--hidden` to the `CustomStateSet` when the state is `true`, and removes it when the state is `false`. -The dashed identifier is just a string preceded by two dashes: in this case we called it `--hidden`, but we could have just as easily called it `--collapsed`. +The setter for the (internal) collapsed state adds the _identifier_ `hidden` to the `CustomStateSet` when the state is `true`, and removes it when the state is `false`. +The identifier is just a string: in this case we called it `hidden`, but we could have just as easily called it `collapsed`. ```js class MyCustomElement extends HTMLElement { @@ -197,16 +197,16 @@ class MyCustomElement extends HTMLElement { } get collapsed() { - return this._internals.states.has("--hidden"); + return this._internals.states.has("hidden"); } set collapsed(flag) { if (flag) { // Existence of identifier corresponds to "true" - this._internals.states.add("--hidden"); + this._internals.states.add("hidden"); } else { // Absence of identifier corresponds to "false" - this._internals.states.delete("--hidden"); + this._internals.states.delete("hidden"); } } } @@ -215,14 +215,14 @@ class MyCustomElement extends HTMLElement { customElements.define("my-custom-element", MyCustomElement); ``` -After adding `` to the HTML we can use the dashed identifier added to the `CustomStateSet`, prefixed with `:`, as a custom state pseudo-class for selecting the element state. -For example, below we select on the `--hidden` state being true (and hence the element's `collapsed` state) using the `:--hidden` selector, and remove the border. +After adding `` to the HTML we can use the identifier added to the `CustomStateSet`, passed to the `:state()` function, as a custom state pseudo-class for selecting the element state. +For example, below we select on the `hidden` state being true (and hence the element's `collapsed` state) using the `:hidden` selector, and remove the border. ```css my-custom-element { border: dashed red; } -my-custom-element:--hidden { +my-custom-element:state(hidden) { border: none; } ```