Convert absolute, font-relative, viewport and container-query units against the context you actually ship. Build fluid clamp() values, inspect existing clamps for accessibility risks, and export the same token model as CSS, Tailwind v4 @theme, Tailwind config, SCSS or DTCG JSON. Everything runs locally in this tab.
{{ item.message }}
{{ contextNotice }}
Most conversions work with these defaults. Open this section when the value depends on a specific element, viewport, font, or query container.
Root font-size sets the rem basis. Local font-size drives em-relative units; viewport and container fields provide the bases for vw, vh and query-container units.
Smaller steps stay static — a 4px gap growing to 5.7px is unreadable code for an invisible effect. The ratio controls how much of each step survives at the small viewport.
Static values only. The fluid range itself lives in the Fluid Builder tab — it is shared with the export.
{{ item.message }}
The slider does not resize anything: vw always reports the real window, so the resolved size below is the same linear function evaluated in JavaScript.
The inspector accepts one fixed term plus one viewport or container term. References to generated Design Tokens are resolved locally from the current Tokens tab; unknown or complex custom properties stay visible and are not guessed.
Your setup is saved in this browser. The share link carries the context and token configuration in its URL hash — opening it sends nothing to a server.
Values follow the Design Tokens tab: {{ outputUnit }} output{{ spacingFluid ? ', fluid spacing' : '' }}, fluid range {{ fluidRange.min }}px → {{ fluidRange.max }}px in {{ fluid.viewportUnit }}.
{{ contextNotice }}
| Unit | Group | Value | 1 unit | Resolves against | Copy |
|---|---|---|---|---|---|
| {{ row.unit }} {{ row.unit }} source | {{ row.group }} | {{ row.available ? format(row.value) + row.unit : '—' }} | {{ row.available ? format(row.basisPx) + 'px' : '—' }} | {{ row.available ? row.basis : 'Unavailable in a media query: there is no query container.' }} estimated |
{{ explanation }}A CSS length is never absolute except in px. Everything else is a fraction of something the browser decides at paint time, which is why the two bases above are printed rather than assumed.
| Token | px | ×base | Declared value | Preview | Copy |
|---|---|---|---|---|---|
| {{ step.token }} | {{ step.px }} | {{ format(step.multiple) }} off grid | {{ declaredSpacing(step) }} |
{{ offGridSteps.length }} step{{ offGridSteps.length === 1 ? '' : 's' }} ({{ offGridSteps.join(', ') }}px) {{ offGridSteps.length === 1 ? 'is' : 'are' }} not a whole multiple of the {{ spacingBase }}px grid. Deliberate half-steps are common — this is a note, not an error.
| Token | px | Declared value | Corner | Copy |
|---|---|---|---|---|
| {{ step.token }} | {{ step.px }} | {{ declaredStatic(step.px) }} |
| Token | Declared value | At {{ fluidRange.min }}px | At {{ fluidRange.max }}px | Copy |
|---|---|---|---|---|
| {{ row.token }} | {{ row.value }} | {{ row.atMin }} | {{ row.atMax }} |
clamp() {{ fluidClamp }}Output unit: {{ outputUnit }}. Change it in the Design Tokens tab; the clamp and export stay in sync.
Both ends are the same size, or the two viewports are — there is nothing to interpolate, so a plain value is emitted instead of a clamp() that only pretends to be fluid.
| Viewport | Resolved | rem | State |
|---|---|---|---|
| {{ row.vw }}px | {{ format(row.px) }}px | {{ format(row.px / safeRoot) }} | {{ row.state }} |
{{ clampInput }}| Reference | Resolved value | State |
|---|---|---|
| {{ tokenRef.token || tokenRef.input }} | {{ tokenRef.css }} | {{ tokenRef.state }} |
| Part | Value | Pixels | Meaning |
|---|---|---|---|
| Minimum | {{ clampParsed.min.value }}{{ clampParsed.min.unit }} | {{ format(clampParsed.min.px) }}px | Lower clamp bound |
| Preferred slope | {{ clampParsed.slope ? clampParsed.slope.value + clampParsed.slope.unit : '—' }} | {{ clampParsed.slope ? 'viewport term' : 'constant' }} | {{ clampParsed.intercept ? 'Viewport-dependent part' : 'No viewport interpolation' }} |
| Fixed intercept | {{ clampParsed.intercept ? clampParsed.intercept.value + clampParsed.intercept.unit : '0px' }} | {{ clampParsed.intercept ? format(clampParsed.intercept.px) + 'px' : '0px' }} | Accessibility anchor |
| Maximum | {{ clampParsed.max.value }}{{ clampParsed.max.unit }} | {{ format(clampParsed.max.px) }}px | Upper clamp bound |
| Viewport | Resolved | rem | State |
|---|---|---|---|
| {{ row.vw }}px | {{ format(row.px) }}px | {{ format(row.px / safeRoot) }} | {{ row.state }} |
What it does. Converts a length between 29 CSS units — print, font-relative, viewport and container-query — and generates the spacing, radius and layout custom properties a design system needs. Both halves share one context, so the number in the converter and the value in the export cannot disagree.
Why the context is editable. Only px is absolute. rem depends on html { font-size } in an element, but media queries use the initial browser font-size instead. em, ch, ex, cap and lh use the local font metrics; container units use the nearest query container. A converter that hardcodes one context answers a question nobody asked.
Tailwind exports. The v4 @theme output uses --spacing for the base scale and named overrides for off-grid or fluid steps, so regular utilities like p-4 keep working. The config output is for older Tailwind projects and produces a ready theme.extend object. SCSS and DTCG JSON preserve the same token model for other pipelines.
svh, lvh and dvh. On mobile the address bar and toolbars retract while scrolling, so a viewport height is really three heights: lvh with the chrome hidden, svh with it shown, and dvh tracking the live value. Set Retractable browser UI to the height of that chrome and the three units separate; leave it at 0 — the desktop case — and they coincide, which is the truth rather than an approximation.
How clamp() is built. A fluid value is a straight line through two points: your small size at the small viewport, your large size at the large one. The middle term is written as a slope in viewport units plus an intercept in rem rather than a bare vw, because a vw-only value ignores the reader's own font-size setting and stops responding to browser zoom. The Clamp Inspector reads existing values back and flags that risk, unreachable bounds, reversed ranges and missing container context.
Nothing you type leaves this browser tab — the whole calculator is a few hundred lines of arithmetic running locally, with no server component and no analytics on your input.