The :state() pseudo-class matches a custom state in an autonomous custom element's ElementInternals.states set. Component JavaScript adds or removes the state identifier; CSS reads it. The selector does not invent the state, persist a decision, or give the element an accessible role by itself.
CSS :state(): expose one internal custom-element state to styling
When to use it
A review-gate element tracks whether a case is awaiting a second reviewer. The component adds the pending-review state through ElementInternals after its domain state changes. An outside stylesheet uses :state(pending-review) to strengthen the border while the component shows the words Pending review in its rendered content. The base border remains clear if the selector is unsupported. Do not encode a server authorization result solely as an internal style token; users and assistive technology need the actual status. Decide whether the state name is a public contract before other teams depend on it. Test state addition, removal, a failed save, a component upgrade after HTML arrives, and a browser without this selector. If the state is reflected to an attribute for compatibility, update that attribute from the same component logic so the two representations cannot disagree. The state should describe the component, not the user's hover or focus, which already have native selectors.
review-gate { display: block; padding: .85rem; color: #173441; border: 1px solid #62808c; }
@supports selector(:state(pending-review)) { review-gate:state(pending-review) { border-inline-start: .4rem solid #915a22; background: #fff4df; } }Cost and verification
The selector adds little rendering cost; the state model and compatibility path cost more. State changes must follow the real workflow, including rollback after a rejected save. A CSS feature gate prevents an unsupported selector from breaking neighboring rules, but it does not create an alternative visible status; the component's text does that work. Custom states are supported on autonomous custom elements, so test the actual element type rather than assuming a customized built-in follows the same pattern. A private state that other applications begin styling becomes an accidental API. Record which identifiers may be used externally and version them with care.
Common Mistakes
- Do not treat :state() as a substitute for status text.
- Do not add a state token before the underlying decision is known.
- Do not assume an unsupported selector leaves the component semantically complete.
