Skip to content
SiteEmail

Prysm supports CSS math functions: clamp(), round(), min(), and max(). These functions can be used anywhere CSS lengths, angles, time, percentages, or numbers are accepted, including inside calc() expressions and with CSS custom properties (var()) populated through Prysm Data-Binding.

All expressions are parsed and evaluated directly in Prysm‘s C++ style engine during layout passes.

Game user interfaces have significantly different architectural requirements than traditional web pages:

  • Zero JavaScript overhead and strict frame budgets: Game UIs typically target 60 or 120 FPS. Running JavaScript resize listeners, ResizeObserver loops, or measuring element bounds (getBoundingClientRect) each frame consumes precious CPU time on the main UI thread. In Prysm, math functions evaluate natively in C++ during the layout pass without JavaScript execution, bridge marshaling, or garbage collection.
  • A single UI for diverse display setups: Games must scale seamlessly across dramatically different viewports—from compact 720p or 800p handhelds (such as the Steam Deck or Nintendo Switch) to 1080p, 1440p, 4K TVs, and 21:9 or 32:9 ultrawide desktop monitors. Instead of maintaining sprawling media queries or resolution-switch scripts, math functions enable fluid, self-bounding UI components.
  • Separation of game logic and presentation: Rather than writing clamping, stepping, or aspect-ratio bounding code in C++ or C# gameplay systems before pushing data to the UI, gameplay code can expose raw numeric state (such as health points, ammo counts, recoil offsets, or compass headings). The stylesheets handle presentation boundaries, percentage clamping, and stepped animations directly.

Game HUDs must remain legible on small handheld screens without becoming excessively large or intrusive on 4K monitors:

.hud-action-bar {
/* Scales fluidly with viewport width, bounded between 320px and 720px */
width: clamp(320px, 40vw, 720px);
/* Ensures font size remains readable on handhelds without overpowering 4K displays */
font-size: clamp(14px, 1.8vw, 24px);
}

Floating nameplates, radar indicators, and cursor tooltips can be constrained within the screen without measuring bounding boxes in JavaScript:

.floating-tooltip {
position: absolute;
/* Keep the 300px-wide tooltip at least 16px away from screen edges */
left: clamp(16px, var(--cursor-x), calc(100vw - 316px));
top: clamp(16px, var(--cursor-y), calc(100vh - 160px));
}

Clamp dynamic game values directly in CSS even if gameplay values momentarily exceed nominal ranges:

.health-bar-fill {
/* Clamps player HP between 0% and 100% directly in CSS */
width: clamp(0%, calc(var(--current-hp) / var(--max-hp) * 100%), 100%);
}

Comply with console certification requirements by establishing guaranteed minimum safe-zone margins that expand proportionally on larger screens:

.hud-container {
/* Guarantees at least 32px safe inset, expanding with large screen resolutions */
padding-top: max(32px, 4vh);
padding-bottom: max(32px, 4vh);
padding-left: max(48px, 5vw);
padding-right: max(48px, 5vw);
}

Ensure dynamic chat windows and inventory containers expand to fit content while maintaining a sensible maximum size relative to the viewport:

.combat-log-panel {
width: min(520px, 90vw);
max-height: min(360px, 45vh);
}

Convert continuous health or armor percentages into discrete segmented visual blocks (such as 10% shield pips) purely through CSS:

.shield-bar-stepped {
/* Snaps continuous shield percentage down to discrete 10% blocks */
width: round(down, var(--shield-pct), 10%);
}

Snap floating navigation compasses, radial weapon wheels, or heading markers to fixed angular steps:

.hud-compass-tape {
/* Snaps compass bearing to 15-degree visual increments */
transform: rotate(round(nearest, var(--heading-deg), 15deg));
}

Pixel-grid snapping for retro and 2D games

Section titled “Pixel-grid snapping for retro and 2D games”

In pixel-art games or stylized retro UIs, sub-pixel floating-point offsets can cause sprite shimmering and blurred glyphs. Snapping dynamic coordinates to virtual pixel or texel grids prevents sub-pixel artifacts:

.pixel-cursor {
/* Snaps element position to a 2px virtual grid to prevent sub-pixel blur */
transform: translate(
round(nearest, var(--target-x), 2px),
round(nearest, var(--target-y), 2px)
);
}

Math functions can be nested within one another or inside calc() to create sophisticated layout rules:

.damage-indicator {
/* Scale and clamp a stepped recoil translation within safe bounds */
transform: translateY(
clamp(-100px, round(nearest, calc(var(--recoil-offset) * -1.2), 4px), 0px)
);
}

Differences from standard behavior and limitations

Section titled “Differences from standard behavior and limitations”

While Prysm implements clamp(), round(), min(), and max() according to the CSS Values and Units Module specification, the following differences and limitations apply:

  • No mixing of percentages and dimensional units in expressions: In alignment with Prysm‘s calc() implementation, mixing percentages (%) with dimensional length units (such as px, em, or vw) inside the same math function expression is not supported. This is because percentages depend on element layout box resolution.
  • No support in @keyframes: Math functions are not supported within @keyframes rule definitions.
  • Argument category compatibility: All arguments within a single math function call must resolve to compatible type categories (for example, lengths with lengths, or angles with angles).

For standard syntax, argument ordering, and specification details, see the MDN references for CSS math functions, clamp(), round(), min(), and max().