Docs
Next

Article

Wrap a page's content in Article to get a readable text column and automatic spacing between the sections inside it.

On this page

Article is the page-level wrapper for your content. It renders an <article> that centres loose content in a readable column, lets sections and full-bleed figures span the page, and decides the spacing between them. Use one per page, inside <main>.

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

export default function Demo() {
  return (
    <Article>
      <Heading.h1>Field notes from the coast</Heading.h1>
      <Paragraph type="lead">
        Loose content in an article sits in a centred column at container width, minus the article
        padding.
      </Paragraph>
      <Paragraph>
        Headings, paragraphs and lists placed directly inside get the rich-text rhythm, so you never
        space them by hand.
      </Paragraph>
      <Section layoutBackground="alternative" padding={2}>
        <Heading.h2>A section spans the full width</Heading.h2>
        <Paragraph>
          Sections break out of the text column and carry their own grid. The article puts one
          section padding of space around them.
        </Paragraph>
      </Section>
      <Paragraph>Back in the article column after the section.</Paragraph>
    </Article>
  )
}

ImportLink to this section

import { Article } from '@systhemaui/next'

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

How it lays out its childrenLink to this section

Article renders <article class="article richtext"> and treats its direct children in two groups:

  • Loose content (headings, paragraphs, lists, quotes) is centred and capped at --container-width minus twice --article-padding-x. It is spaced by the rich-text rhythm.
  • Bands (<section> elements such as Section and Feature, and Figure with width="screen") span the article's full width. .columns, .stack, cards and accordions keep their own width.

The article itself has --section-padding-y of block padding. A band that is the first or last child removes the article's padding on that side, so a page that starts with a full-bleed image has no gap above it.

ExamplesLink to this section

Spacing between sectionsLink to this section

Every section gets one section padding of margin, and two adjacent sections share it. Two neighbouring sections with neither a theme nor a layoutBackground also lose the padding between them, so they read as one band. Give one of them a background when they should read as two. Two adjacent sections that both set the same theme and layoutBackground merge into one band as well.

Spacing between sections
import { Article, Heading, Paragraph, Section } from '@systhemaui/next'

export default function Demo() {
  return (
    <Article>
      <Section padding={2}>
        <Heading.h3>Plain section</Heading.h3>
        <Paragraph>No theme, no background.</Paragraph>
      </Section>
      <Section padding={2}>
        <Heading.h3>Another plain section</Heading.h3>
        <Paragraph>The padding between the two is removed: they read as one band.</Paragraph>
      </Section>
      <Section padding={2} theme="default" layoutBackground="alternative">
        <Heading.h3>A section with a background</Heading.h3>
        <Paragraph>A background starts a new band with its own padding.</Paragraph>
      </Section>
      <Section padding={2} theme="default" layoutBackground="alternative">
        <Heading.h3>Same theme and background again</Heading.h3>
        <Paragraph>
          Matching theme and background on adjacent sections merges them into one band.
        </Paragraph>
      </Section>
    </Article>
  )
}

The full rule set is on the Spacing model page.

Theme and backgroundLink to this section

theme and layoutBackground work as on Section: theme sets data-theme for everything inside, layoutBackground paints the article with the main or alternative page background. A section inside that repeats the article's own theme and background merges into it instead of adding a second band.

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

export default function Demo() {
  return (
    <Article theme="dark" layoutBackground="main">
      <Heading.h2>Night mode for one page</Heading.h2>
      <Paragraph>
        theme="dark" re-resolves every color below the article from the dark color-system mode.
      </Paragraph>
      <Paragraph type="small">With the default tokens, the modes are default and dark.</Paragraph>
    </Article>
  )
}

Starting with a full-bleed imageLink to this section

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

export default function Demo() {
  return (
    <Article>
      <Figure width="screen">
        <Image
          src="/demo-assets/coast.webp"
          alt="Waves breaking on a rocky coast"
          width={1800}
          height={1200}
          aspectRatio="21/9"
        />
      </Figure>
      <Heading.h2>No gap above the image</Heading.h2>
      <Paragraph>
        A screen-width figure as the first child removes the article's top padding.
      </Paragraph>
    </Article>
  )
}

PropsLink to this section

All other props go to the <article> element.

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

HTML and CSSLink to this section

The same structure in plain HTML:

<main id="main-content">
  <article class="article richtext">
    <h1 class="color-heading text-h1">Field notes from the coast</h1>
    <p class="text-body color-body">Loose content sits in the article column.</p>
    <section class="py-section bg-layout-alternative">
      <div class="grid-default container">
        <div class="richtext col-mx-2">…</div>
      </div>
    </section>
  </article>
</main>

The spacing rules are generated per color-system mode and layout background:

ClassStyles
article& { width: 100%; padding-block: var(--section-padding-y, 48px); }&:has( > :first-child:is(section, .figure-w-full, .figure-w-screen)) { padding-top: 0px; }&:has( > :last-child:is(section, .figure-w-full, .figure-w-screen)) { padding-bottom: 0px; }& > *:first-child { margin-top: 0px; }& > *:last-child { margin-bottom: 0px; }+ 20 more rules
bg-layout-mainbackground-color: var(--color-layout-bg-main);
bg-layout-alternativebackground-color: var(--color-layout-bg-alternative);

Next.jsLink to this section

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

AccessibilityLink to this section

<article> has the article role, which is not a landmark. The page landmark is the <main id="main-content"> around it, which the skip link targets. Use one Article per page and start it with the page's single <Heading.h1> (or a hero that renders it). See Landmarks and the skip link.