---
title: "Viewport-relative spacing"
description: "vw, vw-* and spacing-reset for spacing that scales with the viewport."
requested_language: cs
language: en
translation_notice: "This page isn't translated yet"
url: https://docs.systhema.app/cs/next/styling/viewport-units
version: unreleased (main)
docs_index: https://docs.systhema.app/cs/next/llms.txt
---
> This page isn't translated yet. Showing English.


The `vw` utilities make Tailwind's spacing scale fluid inside an element: they redefine the `--spacing` unit as a fraction of the viewport width, so `p-6`, `gap-4`, `mt-10` and every other spacing utility in that subtree grows and shrinks with the screen instead of staying at fixed pixels. `spacing-reset` puts the fixed 4px unit back.

## Quick reference

<!-- generated:utilities viewport-units -->

| Class           | Styles                                                |
| --------------- | ----------------------------------------------------- |
| `vw`            | `--spacing: calc(4 / var(--screen-current) * 100vw);` |
| `vw-1`          | `--spacing: calc(1 / var(--screen-current) * 100vw);` |
| `vw-px`         | `--spacing: calc(1 / var(--screen-current) * 100vw);` |
| `spacing-reset` | `--spacing: 4px;`                                     |
| `vw-1-2xl`      | `--spacing: calc(1 / 1920 * 100vw);`                  |
| `vw-1-3xl`      | `--spacing: calc(1 / 2560 * 100vw);`                  |
| `vw-1-lg`       | `--spacing: calc(1 / 1192 * 100vw);`                  |
| `vw-1-md`       | `--spacing: calc(1 / 768 * 100vw);`                   |
| `vw-1-sm`       | `--spacing: calc(1 / 390 * 100vw);`                   |
| `vw-1-xl`       | `--spacing: calc(1 / 1440 * 100vw);`                  |
| `vw-2xl`        | `--spacing: calc(4 / 1920 * 100vw);`                  |
| `vw-3xl`        | `--spacing: calc(4 / 2560 * 100vw);`                  |
| `vw-lg`         | `--spacing: calc(4 / 1192 * 100vw);`                  |
| `vw-md`         | `--spacing: calc(4 / 768 * 100vw);`                   |
| `vw-px-2xl`     | `--spacing: calc(1 / 1920 * 100vw);`                  |
| `vw-px-3xl`     | `--spacing: calc(1 / 2560 * 100vw);`                  |
| `vw-px-lg`      | `--spacing: calc(1 / 1192 * 100vw);`                  |
| `vw-px-md`      | `--spacing: calc(1 / 768 * 100vw);`                   |
| `vw-px-sm`      | `--spacing: calc(1 / 390 * 100vw);`                   |
| `vw-px-xl`      | `--spacing: calc(1 / 1440 * 100vw);`                  |
| `vw-sm`         | `--spacing: calc(4 / 390 * 100vw);`                   |
| `vw-xl`         | `--spacing: calc(4 / 1440 * 100vw);`                  |

<!-- /generated -->

## Basic usage

`--screen-current` is the design width of the active breakpoint (390, 768 and 1192 with the default tokens). With `vw`, one spacing step is 4 design pixels at that width, so `p-6` is exactly 24px when the viewport matches the design and scales in proportion everywhere else. Because `--spacing` is an inherited custom property, the class affects the element and everything inside it.

Switch the preview between phone, tablet and desktop and watch the two readouts: the fixed box keeps 24px, the `vw` box tracks the viewport and lands on 24px at each design width.

```tsx preview iframe height=360 title="Fixed and fluid padding"
import { useEffect, useRef, useState } from 'react'

function PaddingBox({ fluid }: { fluid: boolean }) {
  const ref = useRef<HTMLDivElement>(null)
  const [padding, setPadding] = useState('')

  useEffect(() => {
    const update = () => {
      if (ref.current) setPadding(getComputedStyle(ref.current).paddingLeft)
    }
    update()
    window.addEventListener('resize', update)
    return () => window.removeEventListener('resize', update)
  }, [])

  return (
    <div className={fluid ? 'vw' : undefined}>
      <div
        ref={ref}
        className="rounded-lg border border-(--color-foundations-surface-border) bg-(--color-foundations-surface-bg) p-6"
      >
        <div className="flex items-baseline justify-between gap-4 rounded bg-(--color-foundations-primary-bg) px-3 py-2 text-(--color-foundations-primary-text)">
          <code className="text-small">{fluid ? 'vw p-6' : 'p-6'}</code>
          <span className="text-small">padding {padding}</span>
        </div>
      </div>
    </div>
  )
}

export default function Demo() {
  return (
    <div className="flex w-full flex-col gap-4">
      <PaddingBox fluid={false} />
      <PaddingBox fluid />
    </div>
  )
}
```

In markup the class goes on a wrapper, and the spacing utilities inside it follow:

```html
<section class="vw">
  <div class="px-6 py-10 gap-4">…</div>
</section>
```

### One design pixel per step

`vw` keeps Tailwind's 4px step, which suits values from a 4px grid. When a design uses arbitrary pixel values, `vw-px` (or its alias `vw-1`) makes one step equal one design pixel, so the number in the class is the number in Figma: `pt-37` is 37 design pixels.

```html
<div class="vw-px pt-37 pb-52 gap-18">…</div>
```

### Pinning one design width

`vw-<breakpoint>` ignores `--screen-current` and divides by a fixed design width instead, for example `vw-lg` scales every step against 1192px at all viewports. The `vw-px-<breakpoint>` and `vw-1-<breakpoint>` forms do the same with one-pixel steps. Use them when a block was drawn only at one size and should scale from that drawing everywhere.

## Responsive and state variants

`vw` re-bases at every breakpoint on its own, because `--screen-current` changes there. To stop fluid spacing from some width up, reset it with a breakpoint prefix:

```html
<section class="vw xl:spacing-reset">
  <!-- fluid up to xl, fixed 4px steps from xl up -->
</section>
```

`spacing-reset` also turns fluid spacing off for one subtree inside a `vw` block, for example a form whose controls should keep their pixel sizes. To pin a design width per breakpoint, combine prefixes: `vw-sm md:vw-md lg:vw-lg` is what `vw` computes on its own; `vw-md lg:vw-md` keeps the tablet drawing on desktops.

## Customizing

The design widths come from `config.screenSizes` in your tokens, and `--screen-current` follows the breakpoints that have a `responsiveSizing` mode. With the default tokens that ends at `lg`, so above 1192px `vw` keeps dividing by 1192 and spacing keeps growing. Defining `customTokens.responsiveSizing.xl` (and up) moves `--screen-current` to 1440 and beyond. The [breakpoints reference](https://docs.systhema.app/cs/next/reference/tokens/breakpoints.md) lists the design width of every breakpoint.

The `vw` classes scale Tailwind's spacing utilities only. Token-driven sizes (container width, grid gaps, section padding) become fluid through the `spacing` rules in `systhema.config`, described in [Responsive sizing](https://docs.systhema.app/cs/next/concepts/responsive-sizing.md#spacing-rules).

> [!NOTE]
> With variable obfuscation on, `--screen-current` and the other `--screen-*` / `--breakpoint-*` variables keep their names, because the `vw` classes read them at runtime. See [Production CSS optimization](https://docs.systhema.app/cs/next/styling/optimization.md#variable-obfuscation).

## Related

- [Responsive sizing](https://docs.systhema.app/cs/next/concepts/responsive-sizing.md)
- [Breakpoints and ranges](https://docs.systhema.app/cs/next/styling/breakpoints.md)
- [CSS variables](https://docs.systhema.app/cs/next/styling/css-variables.md)
- [Breakpoints reference](https://docs.systhema.app/cs/next/reference/tokens/breakpoints.md)
