Sizing & layout.
Fit the content. Reach the controls.
Independent size contracts keep controls predictable while containers, viewport thresholds, and gutters make layouts adapt.
Six contracts, one foundation
Spacing describes relationships. Sizing describes the box that holds or activates content. Density changes spacing only: it never rewrites control heights, icon sizes, interaction targets, viewport breakpoints, or gutters. The values below are Dixy’s initial web contracts, chosen to extend the existing control scale.
| Family | Token | Reference size | CSS value |
|---|---|---|---|
control | --dixy-size-control-sm | 32px | 2rem |
control | --dixy-size-control-md | 40px | 2.5rem |
control | --dixy-size-control-lg | 48px | 3rem |
icon | --dixy-size-icon-xs | 12px | 0.75rem |
icon | --dixy-size-icon-sm | 16px | 1rem |
icon | --dixy-size-icon-md | 20px | 1.25rem |
icon | --dixy-size-icon-lg | 24px | 1.5rem |
icon | --dixy-size-icon-xl | 32px | 2rem |
icon | --dixy-size-icon-2xl | 48px | 3rem |
target | --dixy-size-target-minimum | 24px | max(24px, 1.5rem) |
target | --dixy-size-target-comfortable | 44px | max(44px, 2.75rem) |
Controls retain sm / md / lg heights of 32 / 40 / 48px. Icons use 12 / 16 / 20 / 24 / 32 / 48px; an icon’s size does not define its hit area. Button icons are 16px, or 20px on large buttons. IconButton’s actual box is at least 44 × 44px, even when its size prop is sm. Loading icons use the 16px token.
Try the complete layout
A workspace that fits
Container gutters follow the viewport. Control size stays independent of density.
No action yet.
import "@dixy-ui/react/styles.css";
import { Theme } from "@dixy-ui/react";
import { useState, type CSSProperties } from "react";
import {
Button,
Container,
Field,
Heading,
IconButton,
Inline,
Stack,
Text,
TextField,
Theme as DensityScope,
} from "@dixy-ui/react";Interaction targets have their own floor
The minimum token uses a 24 CSS pixel floor; comfortable uses a 44 CSS pixel floor. Both grow with the root font size. These floors survive smaller root fonts, and remain independent of density. Buttons and inputs use the minimum policy by default; icon buttons always use the larger floor. On devices reporting any coarse pointer, buttons and inputs also use at least the comfortable target. The policy enlarges the actual box, so adjacent targets do not overlap through invisible pseudo-elements.
The WCAG 2.2 minimum target criterion uses 24 × 24 CSS pixels with exceptions; the enhanced criterion uses 44 × 44. Token adoption alone does not establish conformance: content, spacing, focus, labels, and real device use still need review. The playground’s comfortable policy can test larger boxes on a fine-pointer desktop.
.touch-friendly-region {
--dixy-interaction-target: var(--dixy-size-target-comfortable);
}
/* Apply to a Theme root to carry the override through Dialog portals. */Containers and responsive gutters
| Token | Reference maximum | REM |
|---|---|---|
--dixy-container-sm | 640px | 40rem |
--dixy-container-md | 768px | 48rem |
--dixy-container-lg | 1024px | 64rem |
--dixy-container-xl | 1280px | 80rem |
Container defaults to xl, centers with logical margins, and stays within its parent. Its maximum includes inline padding. Gutters use the existing immutable space scale: base → space.200 (16px), sm → space.300 (24px), lg → space.400(32px). They follow the viewport, including when a Container sits in a narrow parent. These are viewport queries, not container queries.
@import "@dixy-ui/tokens/spacing.css";
@import "@dixy-ui/tokens/layout.css";
.page {
box-sizing: border-box;
inline-size: 100%;
max-inline-size: var(--dixy-container-xl);
margin-inline: auto;
padding-inline: var(--dixy-layout-gutter);
}Container API
Generated from Container’s local sizing contract. Native attributes, typed refs, className, style, and semantic element composition through as are forwarded, as they are for Stack and Inline.
| Prop | Type | Default |
|---|---|---|
size | "sm" | "md" | "lg" | "xl"Maximum inline size, including the responsive gutters. | xl |
Breakpoints are shared thresholds
| Name | Reference pixels | Generated media condition |
|---|---|---|
sm | 640px | (min-width: 40rem) |
md | 768px | (min-width: 48rem) |
lg | 1024px | (min-width: 64rem) |
xl | 1280px | (min-width: 80rem) |
wide | 1536px | (min-width: 96rem) |
CSS custom properties expose threshold values for inspection, but cannot be used in media query conditions. The generator emits literal rem conditions from the canonical breakpoints; JavaScript uses minWidth with matchMedia. Media-query rem units follow the browser’s initial font size, while ordinary rem lengths follow the document root. Choose a threshold when content needs a new arrangement, not by device name. xl and wide remain available without increasing gutters beyond 32px.
import { size, container, layoutGutter, minWidth } from "@dixy-ui/tokens";
const controlHeight = size("control", "md");
const iconSize = size("icon", "sm");
const contentMaximum = container("lg");
const padding = layoutGutter;
// Browser code only; the token module itself is safe for SSR.
const query = window.matchMedia(minWidth("lg"));Adapt without clipping content
Keep inputs fluid, wrap actions, and use min-inline-size: 0 on flex/grid children. Avoid fixed-height content panels. Control tokens define nominal heights; the target policy can increase the rendered height. Keep icons decorative unless they communicate content, name icon-only actions, and preserve visible focus indicators. Test long labels, RTL, enlarged text, 320px reflow, and real 200%/400% browser zoom. Resizing a viewport is not the same as zooming. These foundations add no animation.
Existing --dixy-height-sm/md/lg overrides remain available as aliases to the new control sizing contract. Override those on Theme roots for legacy control-height customization; use --dixy-size-control-* for new work. No responsive prop engine, new router, or Tailwind dependency is introduced.