Skip to content

Inverse Tokens ​

The problem ​

In a light theme (direction "down"), content is pushed to darker tones for contrast. But when a surface's tone crosses below 50 (dark enough to be "dark-mode-like"), dark content on a dark surface loses readability. The same happens in reverse for dark themes.

The solution ​

Systema generates inverse versions for every content and container entity:

  • inv-content -- uses the base direction's hue (so the color character stays the same) but the flipped direction's tone. If normal content goes darker, inv-content goes lighter.
  • inv-containers -- same principle applied to container backgrounds.

When to use inversions ​

ScenarioUse
Light card on a dark surfaceinv-containers for the card background
White text on a dark buttoninv-content for the text color
Dark overlay with light UI elementsinv-content on overlays
Accessibility mode with forced high contrastinv tokens naturally provide the opposite contrast direction

Inverse collection merging ​

Direct and inverse tokens live in the same Figma variable collection, which keeps the collection count low. Inverse tokens are distinguished by a path prefix:

PrefixMeaning
inv/Inverse content
on-inv/Content computed on an inverse container
inv-on-inv/Inverse content on an inverse container

The standalone inv/ path segment marks inverse content only. Inverse containers are distinguished by an inv-<entity> name segment instead (e.g. inv-containers-0), not an inv/ path prefix.

For example, in the collection color/containers (balanced structure), on surface level 0:

text
enabled/surfaces-0/containers-0/brand          -- direct container token
enabled/surfaces-0/inv-containers-0/brand      -- inverse container token (same collection)

And for content on that surface, in color/content:

text
on-surfaces/surfaces-0/enabled/c-0/brand       -- direct content token
on-surfaces/surfaces-0/inv/enabled/c-0/brand   -- inverse content token (same collection)

Contrast values ​

Inverse tokens may show different contrast values than their direct counterparts. This is because the flipped tone direction may hit the gamut ceiling (tone 0 or 100) sooner. The preview shows the real measured contrast for both direct and inverse swatches, computed from the actual rendered tones via contrastRatio() in src/lib/hct-engine.ts — Material's Contrast.ratioOfTones() under the WCAG model, or measured APCA Lc under the APCA model (APCA is "coming soon"). The displayed number follows the theme's active contrast model.

Fixed tokens ​

Fixed tokens are a mode-independent companion to the inverse system. They apply to Frame and top-layer entities only.

  • fixedEnabled is the master switch and is off by default.
  • When enabled, a Fixed seed colour and direction override the active mode, so the fixed tokens stay constant regardless of which mode is active.
  • fixedConfig chooses between inheriting the active settings or using a custom configuration. Custom uses fixedContrastRatios and fixedColorIds instead of the inherited values.
  • Fixed tokens land in single-mode color/fixed/{entityName} collections.
  • Fixed also emits inverse variants under an inv prefix, mirroring the inverse convention above.