Spacing.
Give the details room to breathe.
Numeric primitives set the rhythm. Semantic relationships and density give each layout the room it needs.
The primitive scale
The suffix is a percentage of an 8px base unit: space.100 is 8px and space.200 is 16px. The 16 steps include zero, fine 2px adjustments, and 96px / 160px layout extensions. Values use rem; pixel labels assume a 16px root. Appearance and density never rewrite these primitives.
.example {
padding-inline: var(--dixy-space-200);
}Meaning between the numbers
Primitives describe a value. Semantic tokens describe a relationship. Component aliases describe a local decision, such as --dixy-card-padding → --dixy-spacing-inset-md → --dixy-space-200. Change semantic mappings on a density scope to customize relationships.
| Family | Use | Size → primitive |
|---|---|---|
spacing.inline.* | Related items in a row | xs → space.025sm → space.050md → space.100lg → space.150xl → space.200 |
spacing.stack.* | Related items in a column | xs → space.050sm → space.100md → space.200lg → space.300xl → space.400 |
spacing.section.* | Major content sections | sm → space.400md → space.600lg → space.800xl → space.1200 |
spacing.inset.* | Internal container padding | xs → space.100sm → space.150md → space.200lg → space.300xl → space.400 |
Use inline xs–sm for icon/label relationships, stack sm–md for fields and groups, and section sm–xl for major regions. Section and inline mappings stay fixed across the initial density modes. Inset and stack use explicit adjacent scale steps, with no global multiplier.
Try density and layout
Your workspace
Spacing follows density. Controls keep their height.
import { Button, Heading, Inline, Stack, Text, Theme } from "@dixy-ui/react";
import "@dixy-ui/react/styles.css";
export function Settings() {
return (
<Theme density="default" dir="ltr">
<Stack gap="md" style={{ padding: "var(--dixy-card-padding)" }}>
<Heading as="h2" size="sm">Your workspace</Heading>
<Text>Spacing follows density. Controls keep their height.</Text>
<Inline gap="sm" align="center">
<Button>Save changes</Button>
<Button variant="outline">Cancel</Button>
</Inline>
</Stack>
</Theme>Compare all three
A shared 40px control height.
A shared 40px control height.
A shared 40px control height.
Nested scopes
Compact parent · 12px inset
Comfortable child · 20px inset
<Theme density="compact">
<Theme density="comfortable">
<Stack gap="md">Comfortable spacing inside a compact layout.</Stack>
</Theme>
</Theme>Plain HTML can use data-dixy-density="compact", "default", or "comfortable" at any nested boundary. Use Theme’s density prop in React so Dialog portals receive the same density. CSS ancestor selectors alone do not propagate across portals. A nested default scope resets all semantic mappings. Component aliases are rebound at each boundary.
CSS and TypeScript
The token package has no React or Tailwind dependency. React’s stylesheet already includes the spacing foundation; standalone consumers import it directly. Helpers return CSS references, so density continues to work at runtime. Put custom semantic mappings on the density root.
@import "@dixy-ui/tokens/spacing.css";
.settings[data-dixy-density] {
--dixy-spacing-inset-md: var(--dixy-space-250);
}
.settings {
padding-inline: var(--dixy-spacing-inset-md);
padding-block: var(--dixy-spacing-inset-sm);
gap: var(--dixy-spacing-stack-md);
}import { space, spacing, spacingPrimitives, type SpaceToken, type Density } from "@dixy-ui/tokens";
const token: SpaceToken = "200";
const density: Density = "compact";
const padding = space(token); // "var(--dixy-space-200)"
const gap = spacing("stack", "md"); // semantic, density-aware
const referencePixels = spacingPrimitives[token]; // 16Stack and Inline contracts
Stack defaults to gap="md" from spacing.stack; Inline defaults to gap="sm", align="center", and wrap=true from spacing.inline. Both accept xs, sm, md, lg, xl, native attributes, refs, className and style. Choose a semantic HTML element with as, such as section, nav, ul, or form. Each primitive renders one element. Inline wraps by default and follows the element’s writing direction. Style overrides take precedence over the generated gap rules.
Tailwind integration
This project uses plain CSS. Tailwind is optional and is not installed. In a Tailwind v4 consumer, add only the tokens you use to its theme; leave the default spacing scale intact. This example is provided for integration and has not been compiled against Tailwind in this repository.
@import "tailwindcss";
@import "@dixy-ui/tokens/spacing.css";
@theme inline {
--spacing-dixy-100: var(--dixy-space-100);
--spacing-dixy-200: var(--dixy-space-200);
}
/* <div class="p-dixy-200 gap-dixy-100"></div> */Spacing, sizing, and optical adjustments
Spacing defines relationships around content. Control heights, icon sizes, focus rings and minimum interaction targets are separate sizing decisions. Compact density must not shrink those targets or typography. Prefer logical padding and margin properties for RTL, wrapping layouts on narrow screens, and rem values that follow the reader’s root font size. Test reflow and browser zoom at 200% and 400% with real content.
Use a 2px or 6px primitive for a deliberate optical adjustment and explain the exception locally. Avoid arbitrary values when a token fits. Negative gaps and padding are invalid; no negative token variants are provided. An explicit Bleed primitive and negative-margin use cases remain future work. Radius stays an independent Theme setting documented under Theming.
Migrating the alpha scale
Replace --dixy-space-1…8 with --dixy-space-050, -100, -150, -200, -300, -400, -600, and -800 respectively. Prefer semantic aliases in components. The old names are removed to keep one numeric vocabulary. Button gap now follows inline.sm (4px), input padding follows inset.sm (12px), and dialog padding follows inset.lg (24px) at the default density.