Row
Apply the row patch to any block element to lay out its children as a horizontal flex row with spacing between them, vertically centered by default — the general-purpose primitive for icon+label rows, field rows, and button groups, instead of hand-rolling display: flex; alignItems: center; gap: .... toolbar is a semantic alias of this same shape for headers/nav bars; reach for row directly when you need justify/wrap/a non-center align.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
gap | number | 4 | Spacing multiplier for gap between items. Final gap = themeSpacing(density × gap); at default density (1.5), gap 4 ≈ 1.5em. |
align | "flex-start" | "center" | "flex-end" | "stretch" | "baseline" | "center" | Cross-axis alignment (alignItems). |
justify | "flex-start" | "center" | "flex-end" | "space-between" | "space-around" | "space-evenly" | unset | Main-axis distribution (justifyContent). Left unset by default (flex's own default, flex-start). |
wrap | boolean | false | Allow items to wrap onto multiple lines (flexWrap: "wrap"). |
Example
import { row } from "@domphy/ui";
const Field = {
div: [{ label: "Name" }, { input: null, placeholder: "Jane" }],
$: [row({ justify: "space-between" })],
};Customization
Must see the source of patch at the bottom of each patch page to understand the structure then code it still code as html native element.
There are four levels of customization, in increasing order of effort:
- Patch props. Each patch exposes a small, stable set of props—typically fewer than five. Lowest friction.
- Context attributes. Use
dataTone,dataSize, anddataDensityon a container to shift tone, size, or density for an entire subtree without touching individual elements. - Inline override. Native-wins merge strategy: any property set directly on the element overrides the patch value.
- Create a variant. Clone a similar patch and edit it. Use this only when you need a reusable custom version.
Formulas
Unit - U = fontSize / 4 - convert final values with themeSpacing(n).
Size - n = intrinsic text lines, w = wrapping level, d = density factor:
height = (n * 6 + 2 * d * w) * U
paddingBlock = d * w * U
paddingInline = ceil(3 / w) * d * w * U
radius = d * w * UBase density d = 1.5:
| U | w=0 | w=1 | w=2 | w=3 |
|---|---|---|---|---|
height (n = 1) | 6 | 9 | 12 | 15 |
| paddingBlock | 0 | 1.5 | 3 | 4.5 |
| paddingInline | 3 | 4.5 | 6 | 4.5 |
| radius | 0 | 1.5 | 3 | 4.5 |
Tone - K = N / 2 where N is the palette length. For N = 18, K = 9.
| Role | Shift | n=0 |
|---|---|---|
| Background | parent +/- n | 0 |
| Text | bg + K | 6 |
| Border | bg + K/2 | 3 |
| Hover | bg + 2K/3 | 4 |
| Selected / Focus | above +/- K/3 | 2-4 |
State shift range: K/3 <= delta <= 2K/3.
import type { PartialElement } from "@domphy/core";
import { themeDensity, themeSpacing } from "@domphy/theme";
import type { FlexAlign } from "./stack.js";
type FlexJustify =
| "flex-start"
| "center"
| "flex-end"
| "space-between"
| "space-around"
| "space-evenly";
/**
* A horizontal flex row with spacing between children, vertically centered by
* default. The general-purpose primitive for icon+label rows, field rows,
* and button groups — instead of hand-rolling `display: flex; alignItems:
* center; gap: ...`. `toolbar()` is a semantic alias of this same shape for
* headers/nav bars. Styles the host only; apply to any block element.
*
* @param props.gap - Spacing multiplier for gap between items (default 4 = 1em at density 1).
* @param props.align - Cross-axis alignment (`alignItems`). Defaults to `"center"`.
* @param props.justify - Main-axis distribution (`justifyContent`). Unset by default (flex-start).
* @param props.wrap - Allow items to wrap onto multiple lines. Defaults to `false`.
* @example { div: [{ span: "Icon" }, { span: "Label" }], $: [row()] }
* @example { div: [...], $: [row({ justify: "space-between", wrap: true })] }
*/
function row(
props: {
gap?: number;
align?: FlexAlign;
justify?: FlexJustify;
wrap?: boolean;
} = {},
): PartialElement {
const { gap = 4, align = "center", justify, wrap = false } = props;
return {
style: {
display: "flex",
alignItems: align,
gap: (listener) => themeSpacing(themeDensity(listener) * gap),
...(justify ? { justifyContent: justify } : {}),
...(wrap ? { flexWrap: "wrap" } : {}),
},
};
}
export { row };
export type { FlexJustify };