→ A size that scales smoothly with the screen instead of jumping at fixed breakpoints.
Viewport Clamp
A viewport clamp is a size that scales smoothly with the screen instead of jumping at fixed breakpoints — a value that glides between a floor and a ceiling as the viewport grows. defineViewportClamp builds one for you: give it a reference screen size and it emits a native CSS clamp(), so you get fluid sizing with no stair-stepped media queries underneath.
It's the helper I use in every project — in most cases to build responsive variables, and now and then just to drop a fluid one-off value into a component. It's a helper like any other: a function that returns a value, resolved at build time, so the compiled CSS is a plain clamp(...) expression with nothing of Salty's left in it. If you want the deeper why behind that build-time line, the Compiler concept is the read.
What it's for
Most "responsive headline" code is three or four @media blocks doubling a font size at each breakpoint. The result is fine at the breakpoints and a little janky in between — and it's a fresh breakpoint to maintain every time the design shifts. CSS clamp() fixes the jank by scaling the value continuously between a floor and a ceiling. The catch is that typing the right clamp(min, fluid, max) for every size is tedious and easy to get subtly wrong. defineViewportClamp does that arithmetic for you: you give it a reference screen and a value, and it emits the clamp.
The practical payoff is that a size stays proportioned to the layout it was designed in. A headline keeps looking like the design a couple hundred pixels down from where you drew it — no new line breaks appearing, no element suddenly cramped — instead of holding one fixed size until a breakpoint snaps it to another. That means fewer breakpoints to write and far fewer awkward in-between states to babysit.
Two shapes of use cover almost everything:
- Responsive design tokens — the main event. Define your spacing scale and type scale through a clamp, so the whole system scales together. This is where it earns its keep; there's a full worked version below.
- A one-off fluid value — occasionally you just want this padding or this font size to be fluid, inline in a component, without promoting it to a token. That works too.
Defining a clamp
Create a clamp with defineViewportClamp, giving it a reference screen size. Define one per device target you care about — a desktop/HD reference and a mobile reference cover most projects.
import { defineViewportClamp } from "@salty-css/core/helpers";
// Tuned for desktop/HD screens (1920px reference)
export const fhdClamp = defineViewportClamp({
screenSize: 1920,
minMultiplier: 1,
maxMultiplier: 1.25,
});
// Tuned for mobile screens (640px reference)
export const mobileClamp = defineViewportClamp({
screenSize: 640,
minMultiplier: 0.75,
maxMultiplier: 1,
});
// Mobile portrait, scaling on the vertical axis instead of width
export const mobilePortraitClamp = defineViewportClamp({
screenSize: 375,
minMultiplier: 0.75,
maxMultiplier: 1,
axis: "vertical",
});The minMultiplier and maxMultiplier set how far the value is allowed to shrink or grow relative to the value you pass at the call site. A 1 / 1.25 pair means "never smaller than the reference value, up to 25% larger on big screens."
Using a clamp
Call the clamp with the value you want at the reference size. It returns a clamp() string, so it drops into any size property — this is not a font-size-only tool. Margins, padding, gaps, border radius, and positioning all take the same fluid treatment.
import { styled } from "@salty-css/react/styled";
import { fhdClamp, mobileClamp } from "../styles/helpers.css";
export const ResponsiveText = styled("div", {
base: {
fontSize: fhdClamp(96), // 96px at a 1920px viewport, fluid around it
padding: fhdClamp(32),
borderRadius: fhdClamp(8),
// swap to a mobile-tuned clamp below a breakpoint if you want a different curve
"@largeMobileDown": {
fontSize: mobileClamp(48),
},
},
});You don't have to pair it with a media query — a single clamp often carries a value all the way from phone to desktop on its own. The mobile clamp above is there for when you want a genuinely different scaling curve on small screens, not because the base clamp needs rescuing.
How it works
Under the hood the helper builds a CSS clamp(min, fluid, max) where:
- the minimum comes from
minMultiplier(or an explicit min override, below), - the fluid middle scales linearly with the viewport —
vwon the horizontal axis,vhon the vertical, - the maximum comes from
maxMultiplier(or an explicit max override).
So fhdClamp(96) defined with screenSize: 1920, minMultiplier: 1, maxMultiplier: 1.25 resolves to a value that is 96px at a 1920px viewport, can grow to 120px (96 × 1.25) on larger screens, and scales proportionally between. The screenSize is the anchor: it's the viewport width at which the fluid middle exactly equals the value you passed.
You can override the min and max per call, as the second and third arguments — handy for the occasional value that needs a hard floor or ceiling the multipliers don't give:
fhdClamp(96, 42, 240) // exact min 42px, exact max 240px
fhdClamp(96, undefined, 240) // keep the multiplier-based min, override only the maxWorked example
For fhdClamp defined with screenSize: 1920, minMultiplier: 0.5, maxMultiplier: 1.25, calling fhdClamp(96) produces roughly clamp(48px, 5vw, 120px). Resolved at a few common widths:
| Viewport width | Linear value (5vw) | After clamp(48, …, 120) |
|---|---|---|
| 480px | 24px | 48px (min) |
| 1024px | 51.2px | 51.2px |
| 1366px | 68.3px | 68.3px |
| 1920px | 96px | 96px (reference) |
| 2560px | 128px | 120px (max) |
The value glides linearly through the middle and holds flat at both ends. The reference value — 96px at 1920px here — is the point where the fluid formula matches your input exactly.
Building responsive tokens
This is the pattern that makes viewport clamps worth adopting rather than sprinkling. Instead of calling a clamp at every style, call it once inside your variables, so your whole spacing and type system scales fluidly and every component just reads a plain token.
import { defineVariables } from "@salty-css/core/factories";
import { fhdClamp, mobileClamp } from "./helpers.css";
export default defineVariables({
responsive: {
base: {
spacing: {
small: fhdClamp(8),
medium: fhdClamp(20),
large: fhdClamp(36),
pageMargin: fhdClamp(120),
},
fontSize: {
headline: { small: fhdClamp(24), regular: fhdClamp(36), large: fhdClamp(64) },
body: { small: fhdClamp(14), regular: fhdClamp(16), large: fhdClamp(24) },
},
},
"@largeMobileDown": {
spacing: {
pageMargin: mobileClamp(30),
},
fontSize: {
headline: { small: mobileClamp(24), regular: mobileClamp(32), large: mobileClamp(42) },
},
},
},
});Components then reference the tokens by path and never mention a clamp at all:
import { styled } from "@salty-css/react/styled";
export const Header = styled("header", {
base: {
padding: "{spacing.large}",
fontSize: "{fontSize.body.regular}",
},
});Now every dimension in the system scales proportionally, the clamp definitions live in one file, and swapping to mobile-tuned curves at a breakpoint is a change in one place rather than across every component. (For more on the responsive scope itself, see Variables & Tokens.)
Reference
Clamp options — defineViewportClamp(options)
| Option | Description | Default |
|---|---|---|
screenSize | Reference screen width/height in pixels | Required |
minMultiplier | Multiplier for the minimum output (e.g. 0.75 = 75% of value) | Optional |
maxMultiplier | Multiplier for the maximum output (e.g. 1.25 = 125% of value) | Optional |
axis | Axis for scaling — 'horizontal' (vw) or 'vertical' (vh) | 'horizontal' |
Call arguments — clamp(value, min?, max?)
| Argument | Description | Default |
|---|---|---|
0 | Value the property should have at screenSize | Required |
1 | Minimum override (pass undefined to skip) | Optional |
2 | Maximum override | Optional |
Edge cases
- Min greater than max. If your multipliers or overrides flip the order, the browser still honors
clamp(a, b, c)semantics — the larger end wins. Salty doesn't reorder for you, so double-check the multipliers. - Reference size larger than every real viewport. If
screenSize: 1920is bigger than any screen your users have, the value pins tomineverywhere — probably not what you want. DropscreenSizeto the upper end of your actual target range. - Negative multipliers. Allowed (the value can go negative — useful for shifting something off-screen), but rarely what you mean for a size. Use
0if you want the value to collapse to zero on small screens. - Axis selection.
'horizontal'usesvw,'vertical'usesvh. For portrait mobile layouts where height varies more than width, vertical is often the better anchor.
Best practices
Define a small set of clamps — an HD one and a mobile one go a long way — and use them consistently so everything scales in proportion. Set the multipliers to control how much a value may shrink or grow, and reach for the per-call min/max overrides only for the exceptions. And remember it applies to any size property, not just type: unifying spacing, gaps, and radii under the same clamps is what makes a whole layout feel like it scales as one piece.