Version 0.2.0 released! Check out the release notes from GitHub Releases

Fluid Viewport Scaling Compiled at Build — No Hydration Re-Layout, RSC-Friendly Across App Router

Viewport Clamp Across App Router Layouts

defineViewportClamp generates clamp()-based values that scale linearly with the viewport, tuned to a reference screen size. The result is fluid sizing without stair-stepped media queries.

Creating Viewport Clamps

You can define viewport clamps with different reference screen sizes using the defineViewportClamp function:

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

// Clamp function optimized for desktop/HD-sized screens (1920px reference)
export const fhdClamp = defineViewportClamp({
  screenSize: 1920,
  minMultiplier: 1,
  maxMultiplier: 1.25,
});

// Clamp function optimized for mobile-sized screens (640px reference)
export const mobileClamp = defineViewportClamp({
  screenSize: 640,
  minMultiplier: 0.75,
  maxMultiplier: 1,
});

// Clamp function for mobile portrait orientation (uses vertical axis)
export const mobilePortraitClamp = defineViewportClamp({
  screenSize: 375,
  minMultiplier: 0.75,
  maxMultiplier: 1,
  axis: "vertical",
});

Using Viewport Clamps in Components

Once defined, you can use clamp functions in your component styles to create fluid typography and spacing:

import { styled } from "@salty-css/react/styled";
import { fhdClamp, mobileClamp } from "../styles/helpers.css";

export const ResponsiveText = styled("div", {
  base: {
    // Font size will scale fluidly based on viewport width relative to 1920px
    fontSize: fhdClamp(96), // At 1920px wide viewport, this will be 96px

    // Clamp can be applied to any CSS property that accepts size values
    padding: fhdClamp(32),
    borderRadius: fhdClamp(8),

    // Combine with media queries for more complex responsive designs
    "@largeMobileDown": {
      // For mobile, use the mobile-optimized clamp
      fontSize: mobileClamp(48), // At 640px wide viewport, this will be 48px
    },
  },
});

How Viewport Clamp Works

The viewport clamp function creates a CSS clamp() function that:

  1. Sets a minimum size based on the minMultiplier or min value provided as second argument
  2. Applies a fluid formula that scales linearly with the viewport width
  3. Sets a maximum size based on the maxMultiplier or max value provided as third argument

For example, a call to fhdClamp(96) with screenSize:1920, minMultiplier: 1 and maxMultiplier: 1.25 would ensure that:

  • The value will be 96px when screen is at 1920px wide
  • The value can grow up to 120px (96px × 1.25) on larger screens
  • The value scales proportionally with the screen width between these bounds

As another example, fhdClamp(96, 42, 240) with 42 as min override and 240 as max override will:

  • The value will be 96px when screen is at 1920px wide
  • The value can shrink down to 42px as defined to be custom min value
  • The value can grow up to 240px as defined to be custom max value

Hint: If you want to override max but not min you can pass undefined for the min value like this: fhdClamp(96, undefined, 240)

Worked example

For fhdClamp defined with screenSize: 1920, minMultiplier: 0.5, maxMultiplier: 1.25, calling fhdClamp(96) produces approximately clamp(48px, 5vw, 120px). The resolved size at a few common viewport widths:

Viewport widthLinear value (5vw of viewport)After clamp(48, …, 120)
480px24px48px (min)
1024px51.2px51.2px
1366px68.3px68.3px
1920px96px96px (reference)
2560px128px120px (max)

The value scales linearly between min and max, then clamps at both ends. The "reference" value (96px at 1920px in this example) is the point where the linear formula matches your input.

Edge cases

  • Min greater than max. If your multipliers (or explicit overrides) flip the order, the browser still respects clamp(a, b, c) semantics — the larger of the two ends wins. Salty doesn't reorder for you; double-check your multipliers.
  • Reference size larger than common viewports. If screenSize: 1920 is bigger than every viewport your users have, the value is pinned to min everywhere — which is probably not what you want. Drop the screenSize to match the upper end of your target range instead.
  • Negative multipliers. Allowed (the value goes negative), useful for shifting an element off-screen, but rarely what you mean for sizes — start with 0 if you want the value to collapse to zero on small screens.
  • Axis selection. axis: 'horizontal' (default) uses vw; axis: 'vertical' uses vh. For mobile portrait layouts where height varies more than width, vertical is often the right choice.

Configuration Options

When creating a viewport clamp with defineViewportClamp, you can provide several options:

OptionDescriptionDefault
screenSizeReference screen width/height in pixelsRequired
minMultiplierMultiplier for minimum output size (e.g., 0.75 = 75% of value)Optional
maxMultiplierMultiplier for maximum output size (e.g., 1.25 = 125% of value)Optional
axisAxis to use for responsive scaling ('horizontal' or 'vertical')'horizontal'

When using a viewport clamp function you can provide:

Argument indexDescriptionDefault
0Value that should be used in the specified screenSizeRequired
1Minimum value overrideOptional
2Maximum value overrideOptional

Best practices

Define separate clamp instances for different device targets — a 1920px HD clamp and a 640px mobile clamp cover most cases. Set minMultiplier and maxMultiplier to control how much the value can shrink or grow from the reference size. Use the same clamp functions consistently across the project so sizing scales proportionally. Clamps work on any size property, not just font-size — margins, padding, gaps, and positioning all benefit from the same fluid treatment.

Examples

Real-world Usage From This Website

In the website's styles, viewport clamps are used for typography scales:

Responsive Spacing

import { styled } from "@salty-css/react/styled";
import { fhdClamp, mobileClamp } from "../styles/helpers.css";

export const Container = styled("div", {
  base: {
    padding: fhdClamp(24),
    gap: fhdClamp(16),
    marginBottom: fhdClamp(40),

    "@largeMobileDown": {
      padding: mobileClamp(16),
      gap: mobileClamp(12),
      marginBottom: mobileClamp(24),
    },
  },
});

Creating Responsive Design Tokens

Viewport clamps are particularly powerful when used to create responsive design tokens that can be reused throughout your application:

// /styles/variables.css.ts
import { defineVariables } from "@salty-css/core/factories";
import { fhdClamp, mobileClamp } from "./helpers.css";

export default defineVariables({
  // Token with breakpoint-specific values
  responsive: {
    base: {
      spacing: {
        small: fhdClamp(8),
        medium: fhdClamp(20),
        large: fhdClamp(36),
        pageMargin: fhdClamp(120),
        blockMargin: fhdClamp(160),
      },
      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),
        blockMargin: mobileClamp(80),
      },
      fontSize: {
        headline: {
          small: mobileClamp(24),
          regular: mobileClamp(32),
          large: mobileClamp(42),
        },
      },
    },
  },
});

Then use these tokens directly in your component styles:

import { styled } from "@salty-css/react/styled";

export const Card = styled("header", {
  base: {
    // Tokens are referenced using curly braces
    padding: "{spacing.large}",
    fontSize: "{body.regular}",
    background: "#fff",
  },
});

This approach creates a consistent design system where all spacing, sizing, and other dimensions scale proportionally across different viewport sizes. The responsive tokens automatically adapt to different screen sizes based on the media query breakpoints you define.