Docs

This page isn't translated yet

next

Viewport-relative spacing

vw, vw-* and spacing-reset for spacing that scales with the viewport.

On this page

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 referenceLink to this section

ClassStyles
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);

Basic usageLink to this section

--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.

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:

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

One design pixel per stepLink to this section

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.

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

Pinning one design widthLink to this section

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 variantsLink to this section

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:

<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.

CustomizingLink to this section

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 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.