demo · v138

Scroll direction indicator

A signed value --delta drives everything in this stage with no JS branching. sign() rotates an arrow, picks a hue, and switches a label. abs() sets bar width, distance, and intensity. One slider, two reactive panels, twelve coordinated computed values.

Unflagged in Chrome 138. abs() and sign() are part of the CSS Values 4 math functions. Combined with typed arithmetic, also new in v138, they let you do branching that would have required JS or a polyfilled tan(atan2(…)) trick.
probing abs() / sign()…
0
0

Δ change · panel A

+0
color: oklch(from black ... calc(120deg - sign(Δ) * 120deg))
width: calc(abs(Δ) * 1%)

position from origin · panel B

distance 0 direction ·
distance = abs(pos)
direction = sign(pos)  // -1 / 0 / +1

The math

.meter {
  --delta: 12;
  --abs:   abs(var(--delta));      /* 12 */
  --sgn:   sign(var(--delta));     /* +1 (or -1 if delta<0, or 0) */

  /* sign picks the colour band: +1 → green, 0 → black, -1 → red */
  color: oklch(from black calc(50% + abs(var(--delta))/100 * 25%) 0.2
               calc(120deg - sign(var(--delta)) * 120deg));

  /* arrow rotates 0deg (up) when sgn=+1, 360deg (still up) when 0, 180deg when -1 */
  transform: rotate(calc(sign(var(--delta)) * 180deg + 180deg));
}

What's happening

  1. abs(x) returns |x| at the same type as x. So abs(-12px) is 12px — not just a number.
  2. sign(x) returns -1, 0, or +1. Plug it into calc() to multiply something by direction.
  3. Combined, they replace JS branches like delta < 0 ? 'red' : 'green': it's just oklch(... calc(120deg - sign(delta)*120deg)).
  4. Previously this required :has(.negative) selectors, multiple style classes, or a custom property polyfill. With v138 it's one calc.
  5. Combined with typed arithmetic (also new in v138), the same value can drive width, hue, rotation, and translate at once — see the panels above.

see also