Sections and grids

Try it yourself

Main story
Related thought
Example.svelte
svelte
<script>
  import { ContentGrid, ContentBlock } from '@page-sections/svelte';
</script>

<div style="max-width: 100%">
  <ContentGrid>
    <ContentBlock span={{"xs":4,"s":3,"m":5,"l":8}}>Main story</ContentBlock>
    <ContentBlock span={{"xs":4,"s":3,"m":3,"l":4}}>Related thought</ContentBlock>
  </ContentGrid>
</div>

Section gives ordinary content a semantic region, anchor, spacing and optional background. ContentGrid and ContentBlock arrange explicit content inside it. Use them when a page needs an article/aside pair, feature columns or other layouts that vary with available width.

svelte
<script>
  import {
    ContentPage,
    Section,
    ContentGrid,
    ContentBlock
  } from '@page-sections/svelte';
</script>

<ContentPage page={{ title: 'Visit the garden' }}>
  <Section
    section={{
      id: 'visit',
      title: 'Visit',
      headline: 'Make a day of it',
      background: { color: 'muted' }
    }}
  >
    <ContentGrid>
      <ContentBlock span={{ xs: 4, s: 3, m: 5, l: 8 }}>
        <h3>What to bring</h3>
        <p>Comfortable shoes and a little curiosity.</p>
      </ContentBlock>
      <ContentBlock span={{ xs: 4, s: 3, m: 3, l: 4 }}>
        <h3>Opening hours</h3>
        <p>Saturdays, 10 am–2 pm.</p>
      </ContentBlock>
    </ContentGrid>
  </Section>
</ContentPage>

Section props

section: SectionDefinition is required. children?: Snippet supplies content. Section also accepts HTMLAttributes<HTMLElement> except id and the ordinary children attribute; its own class is combined with the library class. Component-owned ID, accessibility and layout attributes are computed from section metadata. Do not depend on overriding them through forwarded attributes.

Complete SectionDefinition

FieldTypeDefault / behavior
idstring, requiredPage-local anchor; registered IDs match ^[a-z][a-z0-9-]*$
titlestring, requiredNonblank navigation/accessibility label
headlinestringNo visible heading unless supplied
eyebrowstringOptional text before heading
introductionstringOptional introductory paragraph
headingLevelHeadingLevel (1-6)Inherited; standalone 2
headingSizeHeadingSize: m, l, xlIndependent of semantics; l by default, m for the content variant
backgroundSectionBackgroundTransparent, standard width
layout'full', 'wide', 'narrow', 'inherit'inherit
spacing'none', 'tight', 'normal', 'loose', 'spacious'normal; nested sections default to none
divider'none', 'top', 'bottom', 'both'none
variant'default', 'content', 'feature'Default grid headings, content headings or larger feature headings
style{ width?: string; color?: string }Optional CSS width/color overrides

layout: 'narrow' caps at --ps-narrow-max (780px); wide caps at the canonical 1536px. Inherit fills the parent. Inside ContentPage, full layout also respects the page's content column and its 1536px limit, including asymmetric sidebar layouts. Use background.size: 'full' to paint a viewport-wide background independently of content width. Outside ContentPage, full layout uses 100vw with centered negative margins. style.width normally caps at the available width.

Vertical padding is 0, 0.5, 1, 2 or 4 times the active grid gap for none/tight/normal/loose/spacious. Inset sections retain one horizontal gutter pair even with spacing none. Dividers are 1px borders using --ps-border. Variants select default grid headings, content headings or larger feature headings.

ContentGrid and ContentBlock props

ComponentProps
ContentGridOptional children: Snippet and standard div HTML attributes, including class/style
ContentBlockThe same, plus optional span: ContentSpan

ContentSpan is number or Partial<Record<ContentGridSize, number>>, where ContentGridSize is xs, s, m, l. Missing spans fill a row. An object inherits the last smaller configured span; before its first configured value, it fills the row. Positive integers clamp to available columns; invalid numbers fill the row. ContentBlock warns about invalid/clamped values in a client effect; these warnings are not development-only.

Section establishes a named container before its inner padding. ContentGrid adds no second outer padding. Standalone grids need a .ps-content-container ancestor to respond to container width; without one, base XS rules apply. Grid rows use normal flow, not dense backfilling, so DOM and keyboard order stay intact.

Section renders the inherited heading level (standalone h2, up to h6) when headline is set; otherwise the region uses title as its accessible label. Standalone sections work without a registry, while sections in ContentPage register and update automatically. Neither grid component introduces a heading or landmark. CSS layout works in the first SSR response.

Related: Responsive Layout and Breakpoints, Section Backgrounds and Surface Groups.

Nested sections and diagnostics

renderHeader defaults to true; disable it only when a specialized renderer supplies the matching headline ID itself. inset defaults to true for top-level sections and false for nested text/panels. Grid renderers set inset=true to establish their own single gutter pair. Nested spacing defaults to none; top-level spacing defaults to normal. Spacing factors none/tight/normal/loose/spacious are 0/0.5/1/2/4 times the active grid gap.

onDiagnostic?: (diagnostic: SectionBackgroundDiagnostic) => void receives normalization diagnostics after hydration, once per requested color/size/context combination. It can update host state. Page kind is inherited from SectionSurfaceGroup; normalization itself also applies during SSR.