CSS
The energy.css reference stylesheet - every class, custom property, and data-attribute selector
import '@kumbatio/energy-system/css'energy.css is the reference CSS for the UI visibility strategy. It is one way to consume energy state - apps can also use the JS strategy API directly and apply styles however they want. The per-level custom property values in the stylesheet mirror uiVisibilityStrategy; applyEnergyLevel sets the same properties as inline styles, which take precedence but resolve to identical values, so the JS and CSS-only paths always agree.
To activate it, set data-energy-level on your root element (body or a container) - manually, via applyEnergyLevel, or via EnergyProvider with applyToDOM.
Classes
Apply these classes to your components:
| Class | Meaning |
|---|---|
.energy-chrome | Any chrome element (titlebar, toolbar, status bar) - fades per level |
.energy-sidebar | Sidebar container - hidden at low levels |
.energy-tab-bar | Tab bar container - hidden at low levels |
.energy-status-bar | Status bar - hidden at low levels |
.energy-toolbar | Editor/app toolbar - hidden only at rest |
.energy-content | Main content area - constrained and scaled at low levels |
Custom properties
Declared on [data-energy-level] with these base values, then overridden per level:
| Property | Base (100) | 75 | 50 | 25 | 0 |
|---|---|---|---|---|---|
--energy-chrome-opacity | 1 | 0.7 | 0.4 | 0.1 | 0.05 |
--energy-chrome-opacity-hover | 1 | 1 | 1 | 1 | 0.8 |
--energy-content-max-width | none | none | 90ch | 80ch | 75ch |
--energy-content-font-scale | 1 | 1 | 1 | 1.05 | 1.1 |
--energy-muted-opacity | 0.5 | 0.5 | 0.5 | 0.5 | 0.5 |
Per-level rules
Level 100 - Peak
No rules beyond the base custom properties: all chrome fully visible.
Level 75 - Active
.energy-chromefades tovar(--energy-chrome-opacity, 0.7)with a0.3s easeopacity transition; restores tovar(--energy-chrome-opacity-hover, 1)on hover.
Level 50 - Steady
.energy-chromefades tovar(--energy-chrome-opacity, 0.4)(same transition and hover restore)..energy-contentgetsmax-width: var(--energy-content-max-width, 90ch)and centeredmargin-inline: auto.
Level 25 - Low
.energy-sidebar,.energy-tab-bar,.energy-status-bararedisplay: none..energy-chromefades tovar(--energy-chrome-opacity, 0.1)(same transition and hover restore)..energy-contentgetsmax-width: var(--energy-content-max-width, 80ch),margin-inline: auto,padding: 2rem, andfont-size: calc(1em * var(--energy-content-font-scale, 1.05)).
Level 0 - Rest
.energy-sidebar,.energy-tab-bar,.energy-status-bar,.energy-toolbararedisplay: none !important..energy-chromefades tovar(--energy-chrome-opacity, 0.05); hover restores only tovar(--energy-chrome-opacity-hover, 0.8)..energy-contentgetsmax-width: var(--energy-content-max-width, 75ch),margin-inline: auto,padding: 2rem,line-height: 1.4,font-size: calc(1em * var(--energy-content-font-scale, 1.1)), andcursor: default !importanton itself and all descendants (the read-only cursor).
Presence gating attributes
The CSS-only path of the presence API. Annotate any element with the energy range it belongs to:
<div data-energy-min="75">AI chat - needs 75+ energy</div>
<div data-energy-max="25">Recovery hint - low energy only</div>The element hides automatically (display: none) whenever the root's data-energy-level falls outside its declared range:
data-energy-min="N"- hidden when the current level is belowN(mirrorspresenceAtOrAbove(N))data-energy-max="N"- hidden when the current level is aboveN(mirrorspresenceAtOrBelow(N))
The rules enumerate every level/threshold comparison explicitly, so both paths agree by definition.
Resolved-presence hooks
Stamp data-energy-presence with the value from resolveEnergyPresence() / useEnergyPresence() when you want CSS to handle the muted treatment:
| Selector | Effect |
|---|---|
[data-energy-presence='muted'] | opacity: var(--energy-muted-opacity, 0.5) |
[data-energy-presence='hidden'] | display: none |
Reduced motion
Presence changes are layout changes; the stylesheet keeps them instant when the user (or OS) asked for reduced motion:
@media (prefers-reduced-motion: reduce) {
[data-energy-level] .energy-chrome {
transition: none;
}
}Apps animating presence transitions themselves should honour the same preference.
