Section

A page section: the page column, the module padding above and below, and an optional surface. Its fill can span the screen or sit inset in the page column. With media it becomes a photo band. It is the scaffold both example sites built for themselves before it existed.

Primitives Section.jsx Section.d.ts Section.md

Playground

Change a prop and watch it. The code below follows along.

Section — The page column, module padding per edge, a tone, an inset band, a scoped dark band, or a photo band.

Install and import

Section is in @dovetail-ds/react. Install it with React, if you haven't:

npm install @dovetail-ds/react react react-dom
import { Section } from "@dovetail-ds/react";

Import @dovetail-ds/react/styles.css once at your app's root, then your theme after it. Setup covers the stylesheets, fonts and dark mode.

Guidelines

A page section: the page column, the module padding above and below, and an optional surface. Its fill can span the screen or sit inset in the page column. With media it becomes a photo band. It is the scaffold both example sites built for themselves before it existed.

Use it when

  • Every top-level band of a marketing or editorial page.
  • A band should be a fixed brand colour, or a photograph with text over it.

Don't use it when

  • Inside an app shell. Product screens are panels and grids, not stacked bands; use Stack and Grid.
  • The image is the content. A photo someone should look at is an Image or a Cover, with alt text.

Example

<Section tone="brand-muted" texture>
  <Stack gap="md">
    <Text variant="eyebrow">Why it holds</Text>
    <Heading level={2}>Three tiers, referenced one way</Heading>
  </Stack>
</Section>

<Section media="/img/ridge.webp" align="bottom" width="wide">
  <Heading level={2} size="display-sm">Quiet mornings</Heading>
  <Text variant="lead">Somewhere without a signal.</Text>
</Section>

Spacing, width and bleed

Every section on a page shares one column, --dt-layout-page-width, so content lines up from page to page without padding and margin doing the work. Configure's Page width moves it for every page at once. width="narrow" and width="wide" read --dt-layout-page-width-narrow and -wide, and full drops the bound. The column keeps --dt-layout-page-gutter from the screen's edge.

spacing sets the padding above and below from the module padding steps: sm, md, lg, xl or none. They move with the layout's character, so a tight page and an open one keep their proportions. spacingTop and spacingBottom set either edge apart, for a band that needs more room on top than below. default and compact are md and sm by their older names.

bleed="inset" sets a band in from the screen's edges: its fill sits in the page column with the container radius, its content is padded by --dt-layout-module-inset, and a block of space keeps two inset bands apart. Use it to set one theme apart from the bands around it.

<Section spacingTop="xl" spacingBottom="md">
  <Heading level={1} size="display-md">Made slowly</Heading>
</Section>

<Section bleed="inset" tone="brand-muted" spacing="lg">
  <Heading level={2}>Members get early access</Heading>
</Section>

Tones

A tone is a surface and the text roles that go on it. The brand tone follows Configure's Fill: solid, gradient, duotone, or Quiet, which is the palest tint of the primary (--dt-surface-brand-muted, its 050 step) with the text that belongs on it, for a band that does not shout. Padding follows the layout's modules setting. To tint a section without re-colouring its text, put data-surface="brand-muted" on it: the surface roles become the brand's tint and text keeps its ordinary roles. Put it on html for the whole page. brand and secondary are full fills; the -muted tones are the pale tint of the same hue. The section re-points --dt-text-primary, -secondary and -tertiary on itself, so everything inside that reads the semantic text roles, Stat and Card descriptions included, follows the band. That is also how to build a band that should not follow the page's light or dark mode: its colours come from the brand roles, not the page surface.

The -muted tones also re-point the buttons inside them. On brand-muted, a primary Button takes the brand colour and a secondary one the secondary brand colour; on secondary-muted the two swap. Each uses the brand-coloured action roles, so its text passes on it.

On a full fill (brand, secondary) the brand can't be the button too, so the section turns its buttons around. A primary or brand Button takes --dt-text-on-brand as its fill and the brand as its label; a secondary one is outlined in the text colour; a ghost one reads in it. Links, --dt-border-subtle and -default, the selected mark and the focus ring move to the text colour as well. Navbar and Drawer use the same declarations for their brand surfaces, and data-surface="brand" does the same for a page or any region.

Dark and photo bands

dark puts the dark class on the section, so the semantic tier and the component tier both re-resolve as dark inside it while the rest of the page stays light. A media band is scoped dark by default. Its text reads --dt-text-on-scrim, which stays light in both modes, because a scrim over a photograph is dark whatever the page is doing.

A scrim's alpha does not tell you the contrast over a specific photograph. Check it the way guidelines/accessibility.md describes: hide the text, sample the pixels behind where it sat, and take the worst case.

Tokens

--dt-layout-page-width, --dt-layout-page-width-narrow, --dt-layout-page-width-wide, --dt-layout-page-gutter, --dt-layout-module-padding-sm, -md, -lg and -xl, --dt-layout-module-inset, --dt-layout-stack-block, --dt-radius-container, --dt-surface-brand*, --dt-text-on-brand*, --dt-surface-scrim, --dt-text-on-scrim*, --dt-surface-texture.

Props

The typed contract, from Section.d.ts.

import * as React from "react";

/**
 * A page section: the page column, the module padding above and below it, and
 * an optional surface, full bleed or set in from the page edges. Pass `media`
 * and it becomes a photo band with a scrim, scoped dark so everything inside
 * it reads as light on dark.
 */
export interface SectionProps extends React.HTMLAttributes<HTMLElement> {
  /** The column, from the page-width tokens: `narrow` (--dt-layout-page-width-narrow, a reading column), `default` (--dt-layout-page-width, the column every page shares), `wide` (--dt-layout-page-width-wide), or `full` with no bound. Inset, it bounds the band itself. @default "default" */
  width?: "narrow" | "default" | "wide" | "full";
  /**
   * The surface, and the text roles that belong on it. Brand and secondary
   * tones re-point --dt-text-primary, -secondary and -tertiary on the section,
   * so Heading and Text inside follow without a prop. The -muted tones also
   * point a Button's primary and secondary at the brand colours (brand-muted:
   * brand then secondary; secondary-muted: the reverse). Ignored when `media` is set.
   * @default "base"
   */
  tone?: "base" | "subtle" | "brand" | "brand-muted" | "secondary" | "secondary-muted";
  /** Scopes dark mode to this section: every semantic and component colour inside re-resolves as dark. Defaults to true when `media` is set. */
  dark?: boolean;
  /** Layers --dt-surface-texture over the tone. @default false */
  texture?: boolean;
  /** Padding above and below, from the module padding steps (--dt-layout-module-padding-sm to -xl), which move with the layout's character: `sm`, `md`, `lg`, `xl` or `none`. `default` is `md` and `compact` is `sm`, by their older names. @default "default" */
  spacing?: "none" | "sm" | "md" | "lg" | "xl" | "default" | "compact";
  /** Padding above, when it differs from `spacing`: a band can take more room at its top than its foot. */
  spacingTop?: "none" | "sm" | "md" | "lg" | "xl";
  /** Padding below, when it differs from `spacing`. */
  spacingBottom?: "none" | "sm" | "md" | "lg" | "xl";
  /** `full`: the band's fill spans the screen and its content sits in the column. `inset`: the band sits in the page column, set in from the screen's edges by the gutter, with the container radius, and pads its content with --dt-layout-module-inset. @default "full" */
  bleed?: "full" | "inset";
  /** Image URL. Turns the section into a full-bleed photo band. The image is decorative; say what matters in the text. */
  media?: string;
  /** With media: gradient fades from the edge the content sits on, solid washes the whole band. @default "gradient" */
  scrim?: "gradient" | "solid" | "none";
  /** With media: where the content sits. @default "bottom" */
  align?: "top" | "center" | "bottom";
  /** With media: the band's minimum height. @default "min(70vh, 640px)" */
  minHeight?: string;
  /** @default "section" */
  as?: keyof React.JSX.IntrinsicElements;
  children?: React.ReactNode;
}

export declare function Section(props: SectionProps): React.JSX.Element;

Tokens it reads

Every custom property Section.jsx resolves, read from the source: 48 component, 60 semantic, 1 primitive. Component tier first, because that is the one to reach for.

  • component Its own tier. Override one of these and only this component moves.
  • semantic Shared roles. Override one and everything using that role moves with it.
  • primitive Raw values. A component reading one directly is a deliberate exception.
TokenTierDeclared as
--dt-button-border-widthcomponentvar(--dt-border-width-default)
--dt-button-brand-bgcomponentvar(--dt-surface-action-brand)
--dt-button-brand-bg-activecomponentvar(--dt-surface-action-brand-active)
--dt-button-brand-bg-hovercomponentvar(--dt-surface-action-brand-hover)
--dt-button-brand-bordercomponentvar(--dt-color-transparent)
--dt-button-brand-fgcomponentvar(--dt-text-on-action-brand)
--dt-button-brand-secondary-bgcomponentvar(--dt-surface-action-brand-secondary)
--dt-button-brand-secondary-bg-activecomponentvar(--dt-surface-action-brand-secondary-active)
--dt-button-brand-secondary-bg-hovercomponentvar(--dt-surface-action-brand-secondary-hover)
--dt-button-brand-secondary-bordercomponentvar(--dt-color-transparent)
--dt-button-brand-secondary-fgcomponentvar(--dt-text-on-action-brand-secondary)
--dt-button-danger-bgcomponentvar(--dt-surface-action-danger)
--dt-button-danger-bg-activecomponentvar(--dt-surface-action-danger-active)
--dt-button-danger-bg-hovercomponentvar(--dt-surface-action-danger-hover)
--dt-button-danger-bordercomponentvar(--dt-color-transparent)
--dt-button-danger-fgcomponentvar(--dt-text-on-action-danger)
--dt-button-disabled-bgcomponentvar(--dt-surface-action-disabled)
--dt-button-disabled-bordercomponentvar(--dt-color-transparent)
--dt-button-disabled-fgcomponentvar(--dt-text-on-action-disabled)
--dt-button-font-familycomponentvar(--dt-text-label-md-family)
--dt-button-font-size-lgcomponentvar(--dt-font-size-md)
--dt-button-font-size-mdcomponentvar(--dt-font-size-sm)
--dt-button-font-size-smcomponentvar(--dt-font-size-sm)
--dt-button-font-weightcomponentvar(--dt-font-weight-medium)
--dt-button-gapcomponentvar(--dt-space-inline-xs)
--dt-button-ghost-bgcomponentvar(--dt-surface-action-ghost)
--dt-button-ghost-bg-activecomponentvar(--dt-surface-action-ghost-active)
--dt-button-ghost-bg-hovercomponentvar(--dt-surface-action-ghost-hover)
--dt-button-ghost-bordercomponentvar(--dt-color-transparent)
--dt-button-ghost-fgcomponentvar(--dt-text-on-action-ghost)
--dt-button-height-lgcomponentvar(--dt-size-control-lg)
--dt-button-height-mdcomponentvar(--dt-size-control-md)
--dt-button-height-smcomponentvar(--dt-size-control-sm)
--dt-button-padding-lgcomponentvar(--dt-space-inset-lg)
--dt-button-padding-mdcomponentvar(--dt-space-inset-md)
--dt-button-padding-smcomponentvar(--dt-space-inset-sm)
--dt-button-primary-bgcomponentvar(--dt-surface-action)
--dt-button-primary-bg-activecomponentvar(--dt-surface-action-active)
--dt-button-primary-bg-hovercomponentvar(--dt-surface-action-hover)
--dt-button-primary-bordercomponentvar(--dt-color-transparent)
--dt-button-primary-fgcomponentvar(--dt-text-on-action)
--dt-button-radiuscomponentvar(--dt-radius-pill)
--dt-button-secondary-bgcomponentvar(--dt-surface-action-secondary)
--dt-button-secondary-bg-activecomponentvar(--dt-surface-action-secondary-active)
--dt-button-secondary-bg-hovercomponentvar(--dt-surface-action-secondary-hover)
--dt-button-secondary-bordercomponentvar(--dt-border-action-secondary)
--dt-button-secondary-fgcomponentvar(--dt-text-on-action-secondary)
--dt-button-transitioncomponentvar(--dt-motion-micro)
--dt-border-defaultsemanticvar(--dt-color-neutral-200)
--dt-border-selectedsemanticvar(--dt-color-neutral-900)
--dt-border-subtlesemanticvar(--dt-color-neutral-100)
--dt-focus-ring-colorsemanticvar(--dt-color-neutral-900)
--dt-focus-ring-offset-colorsemanticvar(--dt-surface-base)
--dt-layout-module-insetsemanticvar(--dt-dim-8)
--dt-layout-module-padding-lgsemanticvar(--dt-dim-32)
--dt-layout-module-padding-mdsemanticvar(--dt-layout-module-padding)
--dt-layout-module-padding-smsemanticvar(--dt-space-section-compact)
--dt-layout-module-padding-xlsemanticvar(--dt-dim-40)
--dt-layout-page-guttersemanticvar(--dt-space-gutter)
--dt-layout-page-widthsemanticvar(--dt-size-container-default)
--dt-layout-page-width-narrowsemanticvar(--dt-size-container-narrow)
--dt-layout-page-width-widesemanticvar(--dt-size-container-wide)
--dt-layout-stack-blocksemanticvar(--dt-dim-8)
--dt-radius-containersemanticvar(--dt-radius-raw-16)
--dt-scrim-fullsemanticcolor-mix(in oklab, var(--dt-color-neutral-950) 55%, transparent)
--dt-surface-action-activesemanticvar(--dt-color-neutral-700)
--dt-surface-action-brandsemanticvar(--dt-color-primary-600)
--dt-surface-action-brand-activesemanticvar(--dt-color-primary-800)
--dt-surface-action-brand-hoversemanticvar(--dt-color-primary-700)
--dt-surface-action-brand-secondarysemanticvar(--dt-color-secondary-600)
--dt-surface-action-brand-secondary-activesemanticvar(--dt-color-secondary-800)
--dt-surface-action-brand-secondary-hoversemanticvar(--dt-color-secondary-700)
--dt-surface-action-dangersemanticvar(--dt-color-red-600)
--dt-surface-action-danger-activesemanticvar(--dt-color-red-800)
--dt-surface-action-danger-hoversemanticvar(--dt-color-red-700)
--dt-surface-action-disabledsemanticvar(--dt-color-neutral-100)
--dt-surface-action-ghostsemanticvar(--dt-color-transparent)
--dt-surface-action-ghost-activesemanticvar(--dt-color-neutral-200)
--dt-surface-action-ghost-hoversemanticvar(--dt-color-neutral-100)
--dt-surface-action-hoversemanticvar(--dt-color-neutral-800)
--dt-surface-action-secondarysemanticvar(--dt-color-neutral-100)
--dt-surface-action-secondary-activesemanticvar(--dt-color-neutral-300)
--dt-surface-action-secondary-hoversemanticvar(--dt-color-neutral-200)
--dt-surface-basesemanticvar(--dt-color-white)
--dt-surface-brandsemanticvar(--dt-color-primary-600)
--dt-surface-brand-mutedsemanticvar(--dt-color-primary-050)
--dt-surface-brand-secondarysemanticvar(--dt-color-secondary-600)
--dt-surface-brand-secondary-mutedsemanticvar(--dt-color-secondary-050)
--dt-surface-scrimsemanticoklch(0.145 0.005 264 / 0.5)
--dt-surface-subtlesemanticvar(--dt-color-neutral-050)
--dt-surface-texturesemanticnone
--dt-text-headlinesemanticvar(--dt-text-primary)
--dt-text-on-action-brandsemanticvar(--dt-color-white)
--dt-text-on-action-brand-secondarysemanticvar(--dt-color-white)
--dt-text-on-action-dangersemanticvar(--dt-color-white)
--dt-text-on-action-disabledsemanticvar(--dt-color-neutral-400)
--dt-text-on-action-ghostsemanticvar(--dt-color-neutral-900)
--dt-text-on-action-secondarysemanticvar(--dt-color-neutral-900)
--dt-text-on-brandsemanticvar(--dt-color-white)
--dt-text-on-brand-mutedsemanticvar(--dt-color-primary-900)
--dt-text-on-brand-secondarysemanticvar(--dt-color-white)
--dt-text-on-brand-secondary-mutedsemanticvar(--dt-color-secondary-900)
--dt-text-on-scrimsemanticvar(--dt-color-neutral-050)
--dt-text-on-scrim-secondarysemanticvar(--dt-color-neutral-200)
--dt-text-primarysemanticvar(--dt-color-neutral-900)
--dt-text-secondarysemanticvar(--dt-color-neutral-600)
--dt-text-tertiarysemanticvar(--dt-color-neutral-500)
--dt-dim-container-smprimitive640px

Source

React only, no dependencies, every value a token.

import React from "react";

/* The page column, from the page-width tokens, so every page lines up. */
const WIDTHS = {
  narrow: "var(--dt-layout-page-width-narrow)",
  default: "var(--dt-layout-page-width)",
  wide: "var(--dt-layout-page-width-wide)",
  full: "none",
};
/* The module padding steps, which move with the layout's character. default
   and compact are the md and sm steps by their older names. */
const pad = (step) => `var(--dt-layout-module-padding-${step})`;
const SPACING = { none: "0", sm: pad("sm"), md: pad("md"), lg: pad("lg"), xl: pad("xl"), default: pad("md"), compact: pad("sm") };
const ALIGN = { top: "flex-start", center: "center", bottom: "flex-end" };

/* Each tone is a surface and the text roles that belong on it. The text roles
   are re-pointed on the section itself, so a Heading or Text inside it that
   reads --dt-text-secondary gets the fill's own secondary rather than the
   page's grey. That is the "fixed card" pattern: the band decides its colours,
   and everything inside it follows without a prop. */
function onFill(fg) {
  return {
    "--dt-text-primary": fg,
    "--dt-text-headline": fg,
    "--dt-text-secondary": `color-mix(in oklab, ${fg} 88%, transparent)`,
    "--dt-text-tertiary": `color-mix(in oklab, ${fg} 76%, transparent)`,
    color: fg,
  };
}
/* On a pale brand tint, a Button's primary and secondary take the brand
   colours: the tint's own hue leads, the other brand hue follows. Each pair
   comes from the brand-coloured action roles, whose text passes on them. */
function buttonsIn(lead, follow) {
  const out = {};
  [["primary", lead], ["secondary", follow]].forEach(([variant, hue]) => {
    const role = `--dt-surface-action-${hue}`;
    out[`--dt-button-${variant}-bg`] = `var(${role})`;
    out[`--dt-button-${variant}-bg-hover`] = `var(${role}-hover)`;
    out[`--dt-button-${variant}-bg-active`] = `var(${role}-active)`;
    out[`--dt-button-${variant}-fg`] = `var(--dt-text-on-action-${hue})`;
    out[`--dt-button-${variant}-border`] = "transparent";
  });
  return out;
}
/* On a strong brand fill the brand can't be the button too: a primary (or
   brand) Button turns to the fill's own text colour with the fill as its
   label, a secondary one is outlined in that text colour, and a ghost one
   reads in it. Links, borders, the selected mark and the focus ring move to
   the text colour as well, so nothing on the fill disappears into it. */
function onStrong(fg, fill) {
  const mix = (a, pct, b) => `color-mix(in oklab, ${a} ${pct}%, ${b})`;
  const out = {
    "--dt-text-link": fg,
    "--dt-border-subtle": mix(fg, 18, "transparent"),
    "--dt-border-default": mix(fg, 32, "transparent"),
    "--dt-border-selected": fg,
    "--dt-focus-ring-color": fg,
    "--dt-focus-ring-offset-color": fill,
  };
  ["primary", "brand", "brand-secondary"].forEach((v) => {
    out[`--dt-button-${v}-bg`] = fg;
    out[`--dt-button-${v}-bg-hover`] = mix(fg, 88, fill);
    out[`--dt-button-${v}-bg-active`] = mix(fg, 76, fill);
    out[`--dt-button-${v}-fg`] = fill;
    out[`--dt-button-${v}-border`] = "transparent";
  });
  out["--dt-button-secondary-bg"] = "transparent";
  out["--dt-button-secondary-bg-hover"] = mix(fg, 12, "transparent");
  out["--dt-button-secondary-bg-active"] = mix(fg, 20, "transparent");
  out["--dt-button-secondary-fg"] = fg;
  out["--dt-button-secondary-border"] = mix(fg, 55, "transparent");
  out["--dt-button-ghost-bg-hover"] = mix(fg, 12, "transparent");
  out["--dt-button-ghost-bg-active"] = mix(fg, 20, "transparent");
  out["--dt-button-ghost-fg"] = fg;
  return out;
}
const TONES = {
  base: { background: "var(--dt-surface-base)" },
  subtle: { background: "var(--dt-surface-subtle)" },
  brand: { background: "var(--dt-surface-brand)", ...onFill("var(--dt-text-on-brand)"), ...onStrong("var(--dt-text-on-brand)", "var(--dt-surface-brand)") },
  "brand-muted": { background: "var(--dt-surface-brand-muted)", ...onFill("var(--dt-text-on-brand-muted)"), ...buttonsIn("brand", "brand-secondary") },
  secondary: { background: "var(--dt-surface-brand-secondary)", ...onFill("var(--dt-text-on-brand-secondary)"), ...onStrong("var(--dt-text-on-brand-secondary)", "var(--dt-surface-brand-secondary)") },
  "secondary-muted": { background: "var(--dt-surface-brand-secondary-muted)", ...onFill("var(--dt-text-on-brand-secondary-muted)"), ...buttonsIn("brand-secondary", "brand") },
};
TONES["brand-secondary"] = TONES.secondary;
TONES["brand-secondary-muted"] = TONES["secondary-muted"];

/* A brand fill's background and every role that has to follow it: text,
   links, borders, focus and buttons. Section uses it for its tones; other
   bands that take a brand fill (Navbar, the open menu in Drawer) use the same
   declarations, so a Button reads the same on every brand fill. Unknown
   tones give the base surface. */
export function fillTone(tone) {
  return TONES[tone] || TONES.base;
}

function scrimImage(scrim, align) {
  if (scrim === "none") return "none";
  if (scrim === "solid" || align === "center") return "var(--dt-scrim-full, var(--dt-surface-scrim))";
  const to = align === "bottom" ? "to top" : "to bottom";
  /* A band's text block runs taller than a card's caption, so the scrim holds
     at full strength for its first third before it fades. */
  return `linear-gradient(${to}, var(--dt-scrim-full, var(--dt-surface-scrim)) 30%, transparent 90%)`;
}

export function Section({
  width = "default",
  tone = "base",
  dark,
  texture = false,
  spacing = "default",
  spacingTop,
  spacingBottom,
  bleed = "full",
  media,
  scrim = "gradient",
  align = "bottom",
  minHeight,
  as: Tag = "section",
  className,
  children,
  style,
  ...rest
}) {
  const photo = !!media;
  const scoped = dark === undefined ? photo : dark;
  const surface = photo ? { background: "var(--dt-surface-base)", ...onFill("var(--dt-text-on-scrim)"), "--dt-text-secondary": "var(--dt-text-on-scrim-secondary)" } : TONES[tone] || TONES.base;
  const fill = texture && surface.background ? `var(--dt-surface-texture), ${surface.background}` : surface.background;
  const both = SPACING[spacing] || SPACING.default;
  const inset = bleed === "inset";
  const limit = WIDTHS[width] || WIDTHS.default;

  /* The band: its fill, its photo and its padding above and below. Full
     bleed, it spans the screen; inset, it sits in the page column with the
     container radius, so a page can set one theme apart from the next. */
  const band = {
    position: "relative",
    overflow: photo || inset ? "hidden" : undefined,
    ...surface,
    background: fill,
    color: surface.color || "var(--dt-text-primary)",
    display: photo ? "flex" : undefined,
    flexDirection: photo ? "column" : undefined,
    justifyContent: photo ? ALIGN[align] || ALIGN.bottom : undefined,
    minHeight: minHeight || (photo ? "min(70vh, var(--dt-dim-container-sm))" : undefined),
    paddingTop: SPACING[spacingTop] || both,
    paddingBottom: SPACING[spacingBottom] || both,
  };
  const content = (
    <>
      {photo && <img src={media} alt="" aria-hidden="true" style={{ position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover", display: "block" }} />}
      {photo && scrim !== "none" && <div aria-hidden="true" style={{ position: "absolute", inset: 0, background: scrimImage(scrim, align) }} />}
      <div
        style={{
          position: photo ? "relative" : undefined,
          width: "100%",
          maxWidth: inset ? undefined : limit,
          marginInline: "auto",
          paddingInline: inset ? "var(--dt-layout-module-inset)" : "var(--dt-layout-page-gutter)",
          boxSizing: "border-box",
        }}
      >
        {children}
      </div>
    </>
  );
  const scope = [scoped ? "dark" : null, className].filter(Boolean).join(" ") || undefined;

  if (inset) {
    /* The gutter keeps it off the screen's edge, and a block of space keeps
       two inset bands apart. */
    return (
      <Tag className={className} style={{ paddingInline: "var(--dt-layout-page-gutter)", paddingBlock: "var(--dt-layout-stack-block)", ...style }} {...rest}>
        <div className={scoped ? "dark" : undefined} style={{ ...band, maxWidth: limit, marginInline: "auto", borderRadius: "var(--dt-radius-container)", boxSizing: "border-box" }}>
          {content}
        </div>
      </Tag>
    );
  }

  return (
    <Tag className={scope} style={{ ...band, ...style }} {...rest}>
      {content}
    </Tag>
  );
}

In context

Section also appears on the primitives reference cards, alongside the rest of its family.