--attr-* and --state-*
Write var(--attr-…) or var(--state-…) in a component's <style> and REALM keeps that custom property set on the host, from a prop, an attribute or state.
:host {
--size: var(--attr-size, 1);
width: calc(var(--spacing) * 10 * var(--size));
}
.bar {
width: calc(var(--state-upload-percent, 0) * 1%);
}Names
| Variable | Value |
|---|---|
var(--attr-name) |
The prop whose attribute is name (its current value, not the attribute text), or the host attribute name when no prop uses it |
var(--state-a-b-c) |
The reference #a.b.c. Dashes separate path segments |
Because dashes separate segments, script and state names used in --state-* cannot contain dashes. camelCase works: var(--state-todo-doneCount). Attribute names in --attr-* can contain dashes: var(--attr-max-items).
Values
| Value | Set to |
|---|---|
| string, number, boolean | String(value) |
null, undefined, object, array |
initial, so var(--attr-x, fallback) uses the fallback |
Behaviour
- REALM finds the variables by scanning the text of the component's
<style>and<link rel="stylesheet">elements, so only names written there are set. - They are set as inline custom properties on the host and update when the value changes.
- Each component sets its own variables on its own host, so
--attr-*values do not leak into nested components that use the same names. - To set a custom property on an inner element instead, bind it:
_--progress="#upload.percent". See_bindings.
Example
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>--attr-* and --state-* demo</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
<script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
<import-element from="./progress-pill.html"></import-element>
<progress-pill></progress-pill>
<progress-pill tone="destructive"></progress-pill>
</body>
</html><custom-element name="progress-pill">
<script>
const props = defineProps({ tone: prop.enum(["primary", "destructive"]).default("primary") });
const upload = useState({ percent: 30 });
</script>
<style>
:host {
display: grid;
gap: calc(var(--spacing) * 2);
width: min(calc(var(--spacing) * 80), 100%);
}
.track {
height: calc(var(--spacing) * 3);
border-radius: var(--radius-full);
background: var(--muted);
}
.fill {
height: 100%;
width: calc(var(--state-upload-percent, 0) * 1%);
border-radius: inherit;
background: var(--primary);
}
:host(:state(attr-tone-is-destructive)) .fill {
background: var(--destructive);
}
</style>
<div class="track"><div class="fill"></div></div>
<input type="range" aria-label="Percent" min="0" max="100" _value="#upload.percent" oninput="upload.percent = Number(this.value)">
</custom-element>