Contact Us

Creating a New Section Type

DOC-00073 how-to developer

Overview

Sections are pure content components. They own their content — headings, grids, forms, actions — but they do not own surface concerns (background, vertical padding, border-radius) or container concerns (content width, gutter). Surface and layout are the caller’s responsibility via composition:

  • LayoutSection — full-width band: background + vertical padding + Container (content width + gutter)
  • SurfaceBox — constrained surface: background + caller-applied rounding and padding via Tailwind utilities
  • class — ad-hoc spacing or styling overrides on the component’s root element

This three-layer composition model keeps each concern in exactly one place and gives callers full control over how sections sit within page layouts.

Step 1: Create the Component File

Place in src/core/components/sections/ (// CORE-OWNED). Use PascalCase naming: MySection.astro. Add // CORE-OWNED at the top of the file.

Step 2: Define the Props Interface

Import only the shared types you need from @/lib/section-types. Do not extend SectionProps — that interface is for LayoutSection, not for section content components.

import type {
  SectionAlign,
  SectionHeadingLevel,
  SectionHeadingSize,
} from "@/lib/section-types";

interface Props {
  heading: string;
  // component-specific props
  align?: SectionAlign;
  headingLevel?: SectionHeadingLevel;
  headingSize?: SectionHeadingSize;
  class?: string;
  [key: string]: unknown;
}

Key rules:

  • No background, verticalPadding, contentWidth, gutter, or rounded — these are surface/container concerns owned by the caller
  • No as prop — section components render <div>, not <section>. The caller’s LayoutSection already renders the <section> element, so using <div> avoids nested sections
  • Always include [key: string]: unknown — enables attribute passthrough (id, aria-*, data-*)

Step 3: Destructure with Defaults

const {
  heading,
  align = "left",
  headingLevel = 2,
  headingSize,
  class: className,
  ...rest
} = Astro.props as Props;

The ...rest collects arbitrary HTML/Astro attributes for forwarding to the root element.

Step 4: Structure the Root Element

The root element is a <div> with BEM class, alignment modifier, and rest-spread for attribute passthrough:

<div
  class:list={[
    "my-section",
    align !== "left" && `my-section--align-${align}`,
    className,
  ]}
  {...rest}
>
  <Heading level={headingLevel} size={headingSize} class="my-section__heading">
    {heading}
  </Heading>
  <!-- section content -->
</div>

No surface classes (surface--bg-*), no padding variant classes (--vp-*). The component renders content only.

Step 5: Use Project Primitives

  • Heading for headings (not raw <h> tags)
  • Button for actions
  • Icon for icons
  • Consume semantic tokens (--st-*), never raw color values

Step 6: Add BEM Class Hooks

Root element gets my-section. Children get my-section__element. These are CSS API hooks for // SITE-OWNED style overrides.

Step 7: Styling Approach

  • Tailwind utilities first.
  • Scoped <style> only for compound/complex selectors, variant variable binding, and alignment variants.
  • No @apply.
  • No surface CSS — no surface--bg-* classes, no padding-block variant blocks.

Step 8: Caller Composition

Callers wrap the section with LayoutSection or SurfaceBox to provide surface and container framing:

---
import LayoutSection from "@core/components/sections/LayoutSection.astro";
import MySection from "@core/components/sections/MySection.astro";
---

<!-- Full-width band with background and padding -->
<LayoutSection background="soft" verticalPadding="2xl">
  <MySection heading="Features" items={features} />
</LayoutSection>

<!-- Inside a constrained surface (e.g., a card) -->
<SurfaceBox background="contrast" class="rounded-xl p-lg">
  <MySection heading="Highlights" items={highlights} />
</SurfaceBox>

Existing Section Types for Reference

All section components follow the pure-content pattern. Good starting points:

  • FeatureGrid.astro — card grid with structured data array
  • CallToAction.astro — the only section that retains an as prop (for cases where the caller doesn’t use LayoutSection)
REQ-00045 normative Components shall encapsulate styling and behavior.
REQ-00047 normative Components shall expose stable public APIs.
REQ-00208 implemented The system shall provide the C5 section baseline set (`Hero`, `CallToAction`, `InfoCards`, `FeatureGrid`, `Testimonials`) as reusable section components composed from shared primitives.

Search

Search across pages and articles. Use arrow keys to navigate results.

Search across pages and articles.

Loading search...

Search is unavailable. Please try again later.

    No results for ""

    Try different keywords or fewer words.