Presence
Declare which energy levels a component belongs to - one typed annotation that drives React, the engine, and plain CSS.
Presence answers one question per element: at which energy levels does this belong on screen? The declaration is a plain typed object - one presence value per level - so the same annotation can drive React rendering, engine resolution, or CSS.
The three presence values
type EnergyPresence = 'visible' | 'muted' | 'hidden'visible- rendered normallymuted- rendered but de-emphasized (reduced opacity, secondary styling)hidden- not rendered at all
A complete declaration is an EnergyPresenceMap: a frozen record with an entry for all five levels.
Declaring presence
defineEnergyPresence builds a complete map from a partial spec; unlisted levels fall back to default ('visible' when omitted):
import { defineEnergyPresence } from '@kumbatio/energy-system'
// Hide the AI chat at 50 and below, mute it at 75
const aiChatPresence = defineEnergyPresence({
default: 'visible',
75: 'muted',
50: 'hidden',
25: 'hidden',
0: 'hidden',
})Shorthands
Most declarations are threshold-shaped, so two helpers cover them:
import { presenceAtOrAbove, presenceAtOrBelow } from '@kumbatio/energy-system'
const composerTools = presenceAtOrAbove(50) // hidden at 25 and 0
const aiSidebar = presenceAtOrAbove(75, 'muted') // muted (not hidden) below 75
const recoveryHint = presenceAtOrBelow(25) // low-energy-only affordanceThe second argument sets what the element becomes outside its range ('hidden' by default).
Resolving presence
import { resolveEnergyPresence, isPresenceVisible } from '@kumbatio/energy-system'
resolveEnergyPresence(aiChatPresence, 50) // 'hidden'
isPresenceVisible('muted') // true - only 'hidden' is falseLifting into a strategy
createPresenceStrategy turns a presence map into a regular AdaptationStrategy<EnergyPresence>, so it resolves through the engine like any built-in. The map is validated once at creation, so resolve() can never fail later:
import { createPresenceStrategy } from '@kumbatio/energy-system'
const aiChat = createPresenceStrategy('ai-chat', aiChatPresence)
engine.resolve(aiChat) // 'visible' | 'muted' | 'hidden'The same annotation in every layer
import { EnergyGate, useEnergyPresence } from '@kumbatio/energy-system/react'
// Component form
<EnergyGate presence={aiChatPresence} fallback={<QuietPlaceholder />}>
{(presence) => <AiChatPanel muted={presence === 'muted'} />}
</EnergyGate>
// Hook form
const presence = useEnergyPresence(aiChatPresence)The CSS attribute path and the JS helpers enumerate the same comparisons, so they agree by definition.
Validation
isEnergyPresence(value) type-guards untrusted input. defineEnergyPresence and the shorthands throw on invalid levels or presence values rather than producing a partial map.
Related
- React quickstart -
<EnergyGate>withmin/maxband gating - CSS quickstart - the attribute rules in detail
