Extending Actual
Select intents
data-enhance is a behavior contract, not a closed list. This page registers
its own select-intent behavior with the public
registerEnhancement() API — options declare data-intent, the
enhancement relays the selected option's intent onto the control.
The enhancement is yours
Actual provides the lifecycle; applications register their own behaviors. This is the same API tabs and scrollspy use — the registry finds existing elements, wires injected ones, and calls the cleanup when an element leaves the document.
import { registerEnhancement } from
"https://cdn.jsdelivr.net/npm/actual-css@0.7.0/src/js/enhance.js";
// with the npm package:
// import { registerEnhancement } from "actual-css/js/enhance";
registerEnhancement("select-intent", (select) => {
if (!(select instanceof HTMLSelectElement) || select.multiple) return;
const sync = () => {
const previous = select.dataset.currentIntent;
if (previous) select.classList.remove(previous);
const intent = select.selectedOptions[0]?.dataset.intent;
if (intent) {
select.classList.add(intent);
select.dataset.currentIntent = intent;
} else {
delete select.dataset.currentIntent;
}
};
const controller = new AbortController();
sync();
select.addEventListener("change", sync, { signal: controller.signal });
return () => controller.abort();
});
The module knows nothing about primary/success/warning/danger. It mirrors
the selected option's data-intent onto the control and remembers the class
it added (data-current-intent), removing it on the next change. An option
without data-intent clears back to a plain control.
Semantics on the option
data-intent describes the option; the enhancement relays it onto the
control. Preselects ship their intent class in the markup, so they paint before the
module even loads — the enhancement adopts them at init.
Done; its success class ships in the markup.
Any select size
The recipe rides the existing .sm/.lg size roles and the
control tokens — nothing to restyle per size.
Injected markup, no extra code
registerEnhancement observes the document. A fresh
<select data-enhance="select-intent"> appended below is wired
automatically — the attribute alone is the whole contract.
Watch the propagation
The enhancement only ever touches the currentIntent class it owns. Change
the status select above and the readout follows.
value/selectedIndex change must dispatch change.
Opt-in only
Without the data-enhance="select-intent" token, this identical select
keeps its baseline look no matter which option is chosen — data-intent
on options is meaning, not paint.