Docs
Systhema Design (opens in new tab)
Unreleased

Section

Build a full-width page band on the container grid, inset its content by grid columns, and give it a theme or background.

On this page

Section is the band most pages are built from: a <section> that holds the container grid, with your content in a rich-text column inside it. Use it for any run of headings, paragraphs and block components that should sit on the page grid.

import { Button, Heading, Paragraph, Section } from '@systhemaui/next'

export default function Demo() {
  return (
    <Section layoutBackground="alternative" padding={2}>
      <Paragraph type="label">Our approach</Paragraph>
      <Heading.h2>Design systems that grow with your team</Heading.h2>
      <Paragraph type="lead">
        One set of tokens drives every page, so a new section looks right the moment you add it.
      </Paragraph>
      <Paragraph>
        Headings, paragraphs and block components placed directly inside a section get the token
        rhythm. You write the content; the spacing comes from composition.
      </Paragraph>
      <Paragraph>
        <Button.a href="#" variant="primary">
          Read the case study
        </Button.a>
      </Paragraph>
    </Section>
  )
}

ImportLink to this section

import { Section } from '@systhemaui/next'

In a React app without Next.js, import it from @systhemaui/react.

How it rendersLink to this section

Section always renders three elements:

  • <section class="py-section">: the band. It carries the block padding (--section-padding-y), the optional data-theme and the optional bg-layout-* class.
  • <div class="grid-default container">: the token layout grid at container width.
  • <div class="richtext col-mx-N">: the content column, inset by padding grid columns on each side.

Because the content column is a rich-text box, its direct children are spaced from tokens. A Stack around them would replace that rhythm with a fixed gap, so put headings, paragraphs and blocks straight inside the section. See Spacing model.

ExamplesLink to this section

Column paddingLink to this section

padding insets the content column by that many grid columns on each side, from the md breakpoint up. Below md the content always spans the full grid. Section accepts 0 to 6; the default is 0.

import { Heading, Paragraph, Section } from '@systhemaui/next'

export default function Demo() {
  return (
    <>
      <Section padding={0}>
        <Heading.h3>padding={'{0}'}</Heading.h3>
        <Paragraph>The content spans the whole container grid.</Paragraph>
      </Section>
      <Section padding={2} layoutBackground="alternative">
        <Heading.h3>padding={'{2}'}</Heading.h3>
        <Paragraph>Two grid columns of inset on each side, a comfortable reading width.</Paragraph>
      </Section>
      <Section padding={4}>
        <Heading.h3>padding={'{4}'}</Heading.h3>
        <Paragraph>Four columns on each side, for short statements and quotes.</Paragraph>
      </Section>
    </>
  )
}

Switch the preview to the phone viewport to see every inset collapse to the full width.

Theme and backgroundLink to this section

theme sets data-theme on the band, so everything inside re-resolves its colors from that color-system mode (default or dark with the default tokens). layoutBackground paints the band with the main or alternative page background of the active mode. See Themes and backgrounds.

Theme and background
import { Heading, Paragraph, Section } from '@systhemaui/next'

export default function Demo() {
  return (
    <>
      <Section theme="dark" layoutBackground="main" padding={2}>
        <Heading.h3>A dark band</Heading.h3>
        <Paragraph>theme="dark" switches every color inside the section to the dark mode.</Paragraph>
      </Section>
      <Section theme="dark" layoutBackground="alternative" padding={2}>
        <Heading.h3>The alternative background of the same mode</Heading.h3>
        <Paragraph>Combine both props to step between two surfaces of one mode.</Paragraph>
      </Section>
    </>
  )
}

Full-bleed mediaLink to this section

When a Figure with width="screen" is the first or last child of a section, the section drops its padding on that side, so the image sits flush against the band's edge.

Full-bleed figure
import { Figure, Heading, Image, Paragraph, Section } from '@systhemaui/next'

export default function Demo() {
  return (
    <Section padding={2}>
      <Figure width="screen">
        <Image
          src="/demo-assets/mountains.webp"
          alt="A mountain range at dusk"
          width={1920}
          height={1080}
          aspectRatio="21/9"
        />
      </Figure>
      <Heading.h3>Flush to the top edge</Heading.h3>
      <Paragraph>
        The figure is the first child, so the section has no top padding. The bottom padding stays.
      </Paragraph>
    </Section>
  )
}

Inside an Article, consecutive sections also coordinate their spacing with each other.

PropsLink to this section

padding takes a whole number from 0 to 6. Other values emit no col-mx-* class, so the content column loses its grid placement. All other props go to the <section> element.

PropTypeDefaultDescription
themeColorSystem (dark, default)-
layoutBackgroundLayoutBackground (main, alternative)-
paddingnumber0
…and all <section> attributes

HTML and CSSLink to this section

The same band in plain HTML:

<section class="py-section bg-layout-alternative" data-theme="dark">
  <div class="grid-default container">
    <div class="richtext col-mx-2">
      <h2 class="color-heading text-h2">Design systems that grow with your team</h2>
      <p class="text-body color-body">One set of tokens drives every page.</p>
    </div>
  </div>
</section>

The column insets are the col-mx-* classes of the grid, and the block padding comes from the section spacing utilities:

ClassStyles
my-sectionmargin-top: var(--section-padding-y);margin-bottom: var(--section-padding-y);
mt-sectionmargin-top: var(--section-padding-y);
mb-sectionmargin-bottom: var(--section-padding-y);
py-section& { padding-top: var(--section-padding-y); padding-bottom: var(--section-padding-y); }&:where(:has( > .container > .richtext > .figure-w-screen:first-child, > .container > .richtext > .figure-w-full:first-child, > .container > .figure-w-screen:first-child, > .container > .figure-w-full:first-child)) { padding-top: 0; }+ 1 more rule
pt-sectionpadding-top: var(--section-padding-y);
pb-sectionpadding-bottom: var(--section-padding-y);

Next.jsLink to this section

@systhemaui/next re-exports Section from @systhemaui/react unchanged. It is a server component.

AccessibilityLink to this section

<section> is only exposed as a region landmark when it has an accessible name. Pass aria-labelledby with the id of the section's heading when the band is a meaningful part of the page outline:

import { Heading, Section } from '@systhemaui/next'

export default function Pricing() {
  return (
    <Section aria-labelledby="pricing-title">
      <Heading.h2 id="pricing-title">Pricing</Heading.h2>
    </Section>
  )
}

A theme="dark" band switches text colors with the background, so contrast stays a token concern.