opsinjs
FoundationsToken familiesSpace and density

Space tokens

The generated reference for the root spacing value, the numeric steps derived from it, the named roles, the target floor and the four mode thresholds.

Overview

Everything the space system knows, in one generated table: the root value, the steps, the named roles that map onto them, the touch-target floor, the minimum separation between adjacent targets, and the four container thresholds that define the responsive modes. Generated from tokens/space.json by scripts/build-tokens.mts.

This is the table most often opened while building rather than while reading, because the Used by column answers the question that actually comes up. That question is not "how big is section" but "what else assumes section, and will I break it".

How it works

tokens/space.json holds four groups.

The root. A single value from which Tailwind derives its whole spacing scale. The product theme sets it above the default; the docs chrome leaves it there. Everything in the steps group is a multiple of it, and the generated table shows the multiple as well as the resolved value.

The named roles. hairline through page, each mapped to a step. Components consume roles; the numeric steps exist underneath them. See Spacing scale.

The floors. --opsin-target-minimum, the minimum touch target, and the minimum separation between adjacent interactive elements. These are the only values in the space system that are not multiples of the root. They are absolute physical minimums and they do not move when the product's density is retuned. That is the point of them.

The mode thresholds. The container widths at which compact, medium, expanded and wide begin, plus the per-mode gutter and the line-measure cap. These are consumed by container queries, not media queries, everywhere except the shell. See Responsive modes.

Using it

Look up the role, not the number. The resolved value changes with the root; the role does not.

Check Used by before changing anything. Space is the most widely consumed token family in the system, and it is arithmetically coupled to radius through the nesting rule. Changing a padding changes a corner.

Notice which rows are absolute. The two floors are deliberately not derived. If you are theming density and a floor moved with it, something is wrong: a product cannot make its own touch targets smaller by choosing a tighter density.

Read the thresholds as container widths. They are not device sizes and not viewport breakpoints. A component in a 300-point pane on a 1600-point display is in compact, and the table is describing the pane.

Tokens

TokenWhat it controlsUsed by
--opsin-space-00rem0px.no component
--opsin-space-10.25remGap between an icon and its label.badge, button, callout, care-card, checkbox, combobox, dialog, dose-tracker, empty-state, field, goal-ring, log-sheet, menu, questionnaire, radio-group, range-bar, result-card, scale-input, scroll-area, segmented-control, select, sheet, slider, source-citation, status-pill, stepper, surface, symptom-picker, tab-bar, tabs, timeline-entry, toast, tooltip, trend-sparkline
--opsin-space-20.5remGap between tightly related lines. For the separation between two interactive targets see targets.separation, which is the same number and carries its scope.alert-banner, avatar, badge, body-map, button, callout, combobox, consent-sheet, dialog, disclaimer-note, dose-tracker, empty-state, field, icon-button, log-sheet, menu, metric-tile, number-field, popover, progress, questionnaire, radio-group, range-bar, range-legend, reading-input, relative-time, scale-input, score-dial, scroll-area, segmented-control, select, sheet, skeleton, slider, spinner, status-pill, surface, symptom-picker, tab-bar, table, tabs, textarea, timeline-entry, toast, tooltip, trend-sparkline, visually-hidden
--opsin-space-30.75remInner padding of a compact control.accordion, alert-banner, button, card, checkbox, combobox, consent-sheet, dialog, disclaimer-note, divider, empty-state, field, log-sheet, menu, metric-tile, popover, progress, radio-group, reading-input, result-card, scroll-area, segmented-control, select, sheet, slider, status-pill, stepper, surface, symptom-picker, tab-bar, table, tabs, textarea, timeline-entry, toast, tooltip, value
--opsin-space-41remThe default gap between elements inside a card.accordion, avatar, badge, button, callout, care-card, consent-sheet, dialog, divider, empty-state, goal-ring, link, log-sheet, menu, popover, range-legend, result-card, scale-input, score-dial, segmented-control, sheet, surface, switch, tabs, timeline-entry, toast
--opsin-space-51.25remCard inner padding on a phone.avatar, button, consent-sheet, dialog, questionnaire, sheet, slider, switch, symptom-picker
--opsin-space-61.5remCard inner padding on a wide screen; gap between cards.avatar, card, dialog, disclaimer-note, empty-state, field, log-sheet, skeleton, stepper, switch, tab-bar, trend-sparkline
--opsin-space-82remGap between sections within a screen.avatar, body-map, disclaimer-note, popover, range-bar, reading-input, sheet, stepper
--opsin-space-102.5remSpace above a section heading.avatar, sheet, skeleton, switch
--opsin-space-123remGap between major regions of a screen.avatar, trend-sparkline
--opsin-space-164remTop of a screen below the safe area; the space a consent sheet leaves above its first question.dialog, skeleton
--opsin-space-205remEmpty-state vertical rhythm.no component
--opsin-space-246remThe largest step. Beyond this, use a layout, not a gap.no component
--opsin-space-px0.0625remHairline borders only.body-map, trend-sparkline
--opsin-space-0-50.125remOptical nudges. Not a layout step.badge, dose-tracker, radio-group, range-legend, result-card, scroll-area, segmented-control, status-pill, stepper, switch, tab-bar, tabs, term, trend-sparkline
--opsin-target-minimum2.75remThe opsinjs floor for any interactive control, applied to the hit area rather than to the visible box.accordion, body-map, button, card, checkbox, combobox, consent-sheet, dialog, disclaimer-note, field, icon-button, menu, metric-tile, number-field, popover, questionnaire, radio-group, reading-input, scale-input, segmented-control, select, sheet, slider, source-citation, switch, symptom-picker, tab-bar, tabs, textarea, toast, tooltip
--opsin-target-comfortable3remThe default for a primary action in the product theme.dialog
--opsin-target-generous3.5remA single primary action on a consent, escalation or emergency surface, where a mis-tap has a real cost.no component
--opsin-target-separation0.5remThe minimum gap between two adjacent targets whose visible boxes are smaller than 44px. It is a floor for undersized targets only. Two targets that both meet the 44px minimum are outside its scope, and separating those is governed by the differing cost of a mis-tap between them rather than by a fixed gap.alert-banner, card, care-card, consent-sheet, result-card
--opsin-gutter-phone16pxThe screen gutter in the phone responsive mode.no component
--opsin-gutter-tablet24pxThe screen gutter in the tablet responsive mode.no component
--opsin-gutter-wide32pxThe screen gutter in the wide responsive mode.no component
--opsin-measure-tight45chA caption or a legend.dialog, empty-state, goal-ring, popover, tooltip
--opsin-measure-comfortable66chThe maximum line length for prose anywhere in the product, including a disclaimer nobody wants to read.disclaimer-note, result-card, score-dial, sheet, source-citation, term
--opsin-measure-wide80chCode and machine output only.no component
--opsin-graphic-dial17remThe maximum width of the ScoreDial arc. This is 16em against the product body of 1.0625rem, which is 17rem and lands on the 4px grid at 68 steps, and it yields to w-full when the column is narrower.goal-ring, score-dial
--opsin-safe-topenv(safe-area-inset-top, 0px)The top safe-area inset, for installed web apps where browser chrome does not protect the edge.no component
--opsin-safe-rightenv(safe-area-inset-right, 0px)The right safe-area inset, for installed web apps where browser chrome does not protect the edge.sheet
--opsin-safe-bottomenv(safe-area-inset-bottom, 0px)The bottom safe-area inset, for installed web apps where browser chrome does not protect the edge.dialog, sheet
--opsin-safe-leftenv(safe-area-inset-left, 0px)The left safe-area inset, for installed web apps where browser chrome does not protect the edge.sheet

Accessibility impact

  • Publishing the floors as tokens is what makes them auditable. A target minimum that lives in one component's stylesheet cannot be checked; one that lives in a generated table can be, and a regression against it fails the build.
  • The mode thresholds are the reflow contract. SC 1.4.10 is tested at 320 CSS pixels, which is inside compact; publishing the threshold makes it possible to say exactly which layout a reader at 400% zoom gets.
  • Derived spacing scales with text; absolute floors do not need to. Both behaviours are correct and the table distinguishes them, which is the only way a reviewer can tell a bug from a decision.
  • The table is a real table, with a header row and navigable cells, as every generated table on this site is.

On this page