Version 0.4.0 now released! See the release notes on GitHub Releases

→ Plain functions that compute a style value, resolved once at build time.

Helpers

A helper is a plain function you import and call to produce a value. Anywhere a style takes a value, you can hand it the result of a helper — fontSize: fhdClamp(96), background: color("#0070f3").darken(0.1) — and Salty treats it exactly like a value you'd typed by hand. The point is reuse: when a value isn't a fixed constant but something you compute — a size that scales, a color derived from another — a helper is the one place that logic lives, so every call site stays a short, readable expression instead of the same arithmetic copied around. You already met the raw idea on the Dynamic Values page, where a style value could be a function; a helper is that idea promoted to a named, reusable tool that solves a recurring problem.

Salty ships two today — defineViewportClamp for fluid clamp() sizing and color() for build-time color manipulation — and more may land over time. Neither is required to ship Salty CSS; reach for one when it solves a problem you actually have. This page covers how helpers work, how to write your own, and a quick tour of the two built-ins. If you want the deeper why behind the "resolved at build time" line that runs through all of it, the Compiler concept is the read.

How helpers work

There's no registration step and no compiler hook. A helper is a function that returns a value, called once while your styles compile, with the result baked straight into the static stylesheet. defineViewportClamp returns a function that returns a clamp() string; color() returns an object whose methods eventually return a color string. That's the whole trick — turns out the wizard behind the curtain is a one-line function. Anticlimactic, and that's the good news, because it means you can write your own the moment a value-producing bit of logic starts repeating.

Take pixel-to-rem conversion. Instead of doing the division in your head at every call site, name it once.

How you define it

/styles/units.ts
// a plain module, no Salty import needed
export const rem = (px: number, base = 16) => `${px / base}rem`;

How you use it

/components/card.css.ts
import { styled } from "@salty-css/react/styled";
import { rem } from "../styles/units";

export const Card = styled("div", {
  base: {
    padding: rem(24),      // → "1.5rem"
    borderRadius: rem(8),  // → "0.5rem"
    fontSize: rem(18),     // → "1.125rem"
  },
});

There's nothing runtime about this. rem is called while the .css.ts file compiles, and the generated CSS contains the literal 1.5rem — the function has already done its job and vanished by the time anything ships. It's the exact mechanism from Dynamic Values (Example 3), with one difference: you named it and exported it, so every component shares one definition instead of each re-deriving the value inline. Change the base once and every call updates.

A couple of habits keep this clean. A pure value-returning helper like rem is just logic, so it's happiest in a plain .ts module that your .css.ts files import — that keeps your style files light, which is the point of the file-sharing note on the Dynamic Values page. And a helper can return anything a style accepts: a clamp() string, a gradient, a full color. That's all defineViewportClamp and color() really are underneath — the same kind of function, with the fiddly math and edge cases already handled for you.

Viewport clamp

Most responsive type is three or four @media blocks bumping a font size at each breakpoint: fine at the breakpoints, a little janky in between, and a new one to maintain every time the design shifts. defineViewportClamp trades the whole stack for a single native clamp() tuned to a reference screen — you author the formula once and call it with the size you want.

/styles/helpers.css.ts
import { defineViewportClamp } from "@salty-css/core/helpers";

export const fhdClamp = defineViewportClamp({ screenSize: 1920, minMultiplier: 0.5, maxMultiplier: 1.25 });
/components/hero.css.ts
import { styled } from "@salty-css/react/styled";
import { fhdClamp } from "../styles/helpers.css";

export const Hero = styled("h1", {
  base: { fontSize: fhdClamp(96) }, // 96px at 1920px, gliding down from there
});

The value scales continuously, so a headline stays proportioned to the layout it was designed in — it keeps looking like the design a couple hundred pixels down instead of holding one size until a breakpoint snaps it. That's fewer breakpoints to write and fewer awkward in-between states to babysit. The Viewport Clamp page covers the full mechanism, the reference options, and the responsive-token pattern it's most at home in.

Color

color() is a chainable helper for lightening, darkening, mixing, fading, and rotating colors. It runs at build time, so the transformed value lands in your CSS as a plain static string — no runtime cost, no color math shipped to the browser.

/components/button.css.ts
import { styled } from "@salty-css/react/styled";
import { color } from "@salty-css/core/helpers";

export const Button = styled("button", {
  base: {
    background: "{colors.brand.primary}",
    "&:hover": { background: color("{colors.brand.primary}").lighten(0.1) },
  },
});

Its most common use is deriving variations of a color for your variables — feed it one brand color and generate a whole family of shades (light, dark, muted, semi-transparent) to store as tokens, instead of hand-picking each hex. The Color Function page has the full method list, the palette-from-one-color pattern, and the build-time boundary to know about.

Going further

Helper, token, template, or modifier?

Helpers overlap with a few other tools, and the line is about what you're reusing:

You want to reuse…Reach for…
A single named value, shared and typed across the systemdefineVariables + {token.path}
A bundle of CSS properties applied togetherdefineTemplates
A new value syntax that rewrites into one or more propertiesModifiers
Logic that computes a value from argumentsA helper (this page) — a function you call to produce the value

The overlap with tokens is the one to get right. If the value is fixed — one brand color, one spacing step — it's a token; you don't need a function to hand back a constant. Reach for a helper when the value depends on an input: a size that varies (rem(24), fhdClamp(96)), a color derived from another (color(brand).darken(0.1)). The two often work together — a helper computes the values a set of tokens is built from, which is exactly the responsive-tokens pattern on the viewport clamp page.

Composing and heavier helpers

Because a helper is just a function returning a valid value, helpers compose. One can call color(), wrap a clamp, or build a whole gradient string from a couple of arguments — whatever produces the value you need. And since they run at build time, a helper can even be async and fetch a value while the site compiles (a color from a CMS, a token from an endpoint); that's the same build-time-function story told in full on the Dynamic Values page, so reach there when a helper needs to read something rather than just compute it. The one guardrail: keep the heavy stuff out of your .css.ts files. Derive the value in a light helper and import the plain result, so the build stays quick.