Domphy

Panel Section

Apply the panelSection patch to any block element for the padded-section chrome common to side-panel and inspector UIs: density-aware padding on all sides, with an optional bottom divider for sections stacked one after another. It's a thin wrapper — it does not impose flex layout on its children, so pair it with stack or row for that.

Props

PropTypeDefaultDescription
paddingnumber4Spacing multiplier for padding on all sides. Final padding = themeSpacing(density × padding); at default density (1.5), padding 41.5em.
dividerbooleanfalseAdds a bottom border, for sections stacked one after another.
colorValueOrState<ThemeColor>"neutral"Theme color tone for the divider border.

Example

import { panelSection, stack } from "@domphy/ui";

const Sidebar = {
  div: [
    { div: [{ h4: "Parameters" }, { p: "..." }], $: [stack({ gap: 1 }), panelSection({ divider: true })] },
    { div: [{ h4: "Operations" }, { p: "..." }], $: [stack({ gap: 1 }), panelSection()] },
  ],
  $: [stack({ gap: 0 })],
};
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:

  1. Patch props. Each patch exposes a small, stable set of props—typically fewer than five. Lowest friction.
  2. Context attributes. Use dataTone, dataSize, and dataDensity on a container to shift tone, size, or density for an entire subtree without touching individual elements.
  3. Inline override. Native-wins merge strategy: any property set directly on the element overrides the patch value.
  4. 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 * U

Base density d = 1.5:

Uw=0w=1w=2w=3
height (n = 1)691215
paddingBlock01.534.5
paddingInline34.564.5
radius01.534.5

Tone - K = N / 2 where N is the palette length. For N = 18, K = 9.

RoleShiftn=0
Backgroundparent +/- n0
Textbg + K6
Borderbg + K/23
Hoverbg + 2K/34
Selected / Focusabove +/- K/32-4

State shift range: K/3 <= delta <= 2K/3.

import { type PartialElement, toState, type ValueOrState } from "@domphy/core";
import {
  type ThemeColor,
  themeColor,
  themeDensity,
  themeSpacing,
} from "@domphy/theme";

/**
 * A padded section for side-panel/inspector UIs — density-aware padding on
 * all sides, with an optional bottom divider for sections stacked one after
 * another. A thin wrapper: it does not impose flex layout on its children —
 * pair it with `stack()` or `row()` for that. No host-tag check; apply to
 * any block element.
 *
 * @param props.padding - Spacing multiplier for padding on all sides (default 4 = 1em at density 1).
 * @param props.divider - Adds a bottom border, for sections stacked one after another. Defaults to `false`.
 * @param props.color - Theme color tone for the divider border. Defaults to `"neutral"`.
 * @example { div: [{ h3: "Parameters" }, { p: "..." }], $: [panelSection()] }
 * @example
 * { div: [
 *   { div: "Section A", $: [panelSection({ divider: true })] },
 *   { div: "Section B", $: [panelSection()] },
 * ], $: [stack({ gap: 0 })] }
 */
function panelSection(
  props: {
    padding?: number;
    divider?: boolean;
    color?: ValueOrState<ThemeColor>;
  } = {},
): PartialElement {
  const { padding = 4, divider = false } = props;
  const color = toState(props.color ?? "neutral", "color");
  return {
    style: {
      padding: (listener) => themeSpacing(themeDensity(listener) * padding),
      ...(divider
        ? {
            borderBottom: (listener) =>
              `1px solid ${themeColor(listener, "border", color.get(listener))}`,
          }
        : {}),
    },
  };
}

export { panelSection };