--attr-*
declarationsince 0.9.0experimentalrequires runtime--attr-<name>: <value> writes one allowlisted HTML attribute of the matched element from a reactive value: ARIA states such as aria-expanded, aria-checked or aria-disabled, disabled, hidden, tabindex or data-*. The only custom-property namespace with DOM semantics.
On this page · 10 sections+−
Syntax
--attr-<attribute>: <value>;registry syntax: --attr-<attribute>: <string> | <number> | <keyword> | var(--state) | when(condition [, a, b])
Parameters
- attributelower-case allowlisted namerequired
aria-expanded,aria-pressed,aria-selected,aria-disabled,aria-hidden,aria-busy,aria-invalid,aria-checked,aria-current,aria-valuenow,aria-label,aria-valuetext,disabled,hidden,required,tabindex(0or-1) ordata-*.- valueone scalarrequired
- A literal,
var(),previous(),direction()orwhen(); a nativeif()also works.when(condition)without branches givestrueorfalse.
Effect
No value. The attribute is written in the same task as the action that changed the state, so a derived disabled is in place before the next click.
Example
.item { --open: state(false);}.item > button:on(click) { --open: toggle();}/* Writes aria-expanded="true" or "false" on the button. */.item > button { --attr-aria-expanded: var(--open);}.item .body { display: none;}.item:when(--open) .body { display: block;}.item .chevron { rotate: when(--open, 180deg, 0deg);}Correct and incorrect usage
correctDisable a button at a limit
.counter { --count: state(0); }.counter .dec:on(click) { --count: decrement(1); }.counter .dec { --attr-disabled: when(--count <= 0); }The disabled attribute is present while the count is 0 or less and removed otherwise, so the button cannot take the count below zero.
correctA state name as a data attribute
.panel { --open: state(false); --attr-data-state: when(--open, "open", "closed");}Writes data-state="open" or data-state="closed" for other CSS or scripts to read.
incorrectA URL attribute
.link { --attr-href: "/next"; }URL attributes are never writable, so nothing is written and the declaration stays a plain custom property.
incorrectAn upper-case suffix
.menu { --open: state(false); }.menu > button { --attr-aria-Expanded: var(--open); }Custom property names are case-sensitive, attribute names are not. Write --attr-aria-expanded.
Behavior
- Experimental as a namespace; the attribute semantics have been tested since 0.5.3.
- Reserved namespace. Every custom property named
--attr-*is claimed by Reactive CSS. An allowlisted name writes that attribute; any other name gives a diagnostic and stays a plain custom property. Without the prefix, names such as--disabledor--aria-expandedhave no DOM effect. A state cannot be named--attr-*. - Values. HTML boolean attributes (
disabled,hidden,required):truesets the attribute andfalseremoves it; a boolean state’s1and0work too. ARIA booleans are written as"true"or"false";aria-checkedalso takesmixed;aria-currenttakespage,step,location,date,time,trueorfalse;aria-valuenowa number;aria-label,aria-valuetextanddata-*any text. Other values are reported and not written. - Restore. With no applicable declaration, or a missing or empty winning value, the attribute returns to what it was before the first write. If something else changed it in the meantime, the binding yields and writes no more.
- One property per attribute. Declarations for different attributes combine, and the cascade picks the winner for each attribute separately.
- No loops. Attributes written this way never re-match reactive rules, and attributes never feed state.
disabledandrequiredare written only where HTML gives them a meaning (a warning otherwise).
Supported contexts
- Style rules in trusted stylesheets, on any element a rule matches. The value usually reads state owned by an ancestor.
- Not inside
:on()rules: an attribute follows a state, it does not happen at an event.
Limitations
- Compatibility warning. A stylesheet that already uses
--attr-*custom properties for something else now writes attributes when the name is allowlisted:--attr-hidden: 1hides the element,--attr-data-xwritesdata-x. Rename such properties before upgrading. This is the one change in 0.9 that can alter a working 0.8 page; see Migration. - Never writable: event handlers (
on*), URL attributes (href,src,action,formaction,srcset,xlink:href),style,id,class,nameandvalue, as well asroleand ID references such asaria-describedby. - The suffix must be lower case. Custom property names are case-sensitive, so
--attr-aria-Expandedis a diagnostic and nothing is written. - A
state(…, bind(<attribute>))on the same element owns that attribute; an--attr-*declaration for it is ignored with a diagnostic. - Setting attributes does not make a component accessible by itself.
Examples
- Attribute sync · API examples, runs the real runtime
- State toggle · API examples, runs the real runtime