Kumbatio

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:

ClassMeaning
.energy-chromeAny chrome element (titlebar, toolbar, status bar) - fades per level
.energy-sidebarSidebar container - hidden at low levels
.energy-tab-barTab bar container - hidden at low levels
.energy-status-barStatus bar - hidden at low levels
.energy-toolbarEditor/app toolbar - hidden only at rest
.energy-contentMain content area - constrained and scaled at low levels

Custom properties

Declared on [data-energy-level] with these base values, then overridden per level:

PropertyBase (100)7550250
--energy-chrome-opacity10.70.40.10.05
--energy-chrome-opacity-hover11110.8
--energy-content-max-widthnonenone90ch80ch75ch
--energy-content-font-scale1111.051.1
--energy-muted-opacity0.50.50.50.50.5

Per-level rules

Level 100 - Peak

No rules beyond the base custom properties: all chrome fully visible.

Level 75 - Active

  • .energy-chrome fades to var(--energy-chrome-opacity, 0.7) with a 0.3s ease opacity transition; restores to var(--energy-chrome-opacity-hover, 1) on hover.

Level 50 - Steady

  • .energy-chrome fades to var(--energy-chrome-opacity, 0.4) (same transition and hover restore).
  • .energy-content gets max-width: var(--energy-content-max-width, 90ch) and centered margin-inline: auto.

Level 25 - Low

  • .energy-sidebar, .energy-tab-bar, .energy-status-bar are display: none.
  • .energy-chrome fades to var(--energy-chrome-opacity, 0.1) (same transition and hover restore).
  • .energy-content gets max-width: var(--energy-content-max-width, 80ch), margin-inline: auto, padding: 2rem, and font-size: calc(1em * var(--energy-content-font-scale, 1.05)).

Level 0 - Rest

  • .energy-sidebar, .energy-tab-bar, .energy-status-bar, .energy-toolbar are display: none !important.
  • .energy-chrome fades to var(--energy-chrome-opacity, 0.05); hover restores only to var(--energy-chrome-opacity-hover, 0.8).
  • .energy-content gets max-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)), and cursor: default !important on 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 below N (mirrors presenceAtOrAbove(N))
  • data-energy-max="N" - hidden when the current level is above N (mirrors presenceAtOrBelow(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:

SelectorEffect
[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.