New sections
Every bundled section is a single .astro file in src/components/. Building a new one is just creating another file. This page walks through the pattern.
The minimum viable section
Section titled “The minimum viable section”A new section starts here:
---interface Props { heading?: string; awards?: Array<{ year: string; title: string; org: string }>;}
const { heading = "Recognition", awards = [ { year: "2025", title: "Product of the Year", org: "Indie Hackers" }, { year: "2024", title: "Webby Award", org: "Webby" }, ],} = Astro.props as Props;---
<section class="py-20 md:py-28"> <div class="container-content"> <h2 class="text-display-sm reveal">{heading}</h2> <ul class="mt-12 grid grid-cols-1 md:grid-cols-3 gap-6"> { awards.map((award) => ( <li class="reveal p-6 bg-surface border border-line-soft rounded-2xl"> <div class="text-eyebrow">{award.year}</div> <div class="mt-2 text-[1.05rem] font-semibold text-strong">{award.title}</div> <div class="mt-1 text-[14px] text-soft">{award.org}</div> </li> )) } </ul> </div></section>Drop it into any page:
import Awards from "~/components/Awards.astro";// …<Awards />It already follows the theme’s spacing, container, surface, and typography conventions.
Use the design tokens
Section titled “Use the design tokens”Every theme exposes a small palette of utility classes that map to brand tokens. Reach for these first — they’re what keep new sections visually consistent with shipped ones.
| Token | Use |
|---|---|
container-content | Standard horizontal container (78rem max) |
container-wide | Wider container (90rem max) |
bg-bg | Page background |
bg-surface | Card / subtle surface |
bg-elevated | Elevated surface (darker than surface) |
bg-brand-soft | Brand-tinted soft background |
text-strong | Headings, emphasized text |
text-text | Default body text |
text-soft | Secondary body |
text-muted | Captions, helper text |
text-brand | Links, accents |
border-line | Strong dividers |
border-line-soft | Subtle dividers |
text-eyebrow | Pre-heading “EYEBROW” treatment |
text-display | Big display headings |
text-display-sm | Section-level display headings |
btn | Base button reset |
btn-brand | Primary brand button |
btn-ghost | Secondary outline button |
btn-lg | Large size modifier |
chip | Small rounded label/badge |
reveal | Add to any element for scroll-reveal in |
Pattern: read from siteConfig
Section titled “Pattern: read from siteConfig”If your new section’s data should be editable without touching the component, follow the bundled pattern:
---import { siteConfig } from "~/config/site";
const a = (siteConfig as any).awards; // optional blockconst heading = a?.heading ?? "Recognition";const items = a?.items ?? [];---
{items.length > 0 && ( <section class="py-20 md:py-28"> {/* …same as above, using `heading` and `items`… */} </section>)}Then add the matching block in site.ts:
awards: { heading: "Recognition", items: [ { year: "2025", title: "Product of the Year", org: "Indie Hackers" }, ],},The section auto-hides if items is empty — handy when you reuse the theme for clients who don’t have any.
Pattern: reveal-on-scroll
Section titled “Pattern: reveal-on-scroll”The theme already loads an IntersectionObserver script in Layout.astro that watches for .reveal elements. Add the class, optionally a data-delay:
<div class="reveal" data-delay="1">…</div><div class="reveal" data-delay="2">…</div>data-delay is 1–6; each step is ~80ms. No setup, no JS for you to write.
Pattern: per-section <style> blocks
Section titled “Pattern: per-section <style> blocks”For section-specific styling that doesn’t fit Tailwind utilities, scope it with <style>:
<section class="awards-grid"> {/* … */}</section>
<style> .awards-grid { background: linear-gradient(to bottom, var(--color-bg), var(--color-surface)); }</style>Astro automatically scopes the styles to this component — no class name collisions.
Anatomy of a shipped section
Section titled “Anatomy of a shipped section”Open src/components/Hero.astro or src/components/Pricing.astro from any theme. You’ll find the same skeleton:
---// 1. Importsimport Icon from "~/components/Icon.astro";import { siteConfig } from "~/config/site";
// 2. Props with sensible defaultsconst { heading = "..." } = Astro.props;
// 3. Local computations (optional)const items = siteConfig.something.items;---
<!-- 4. Outer <section> with vertical rhythm classes --><section class="py-20 md:py-28"> <!-- 5. Container --> <div class="container-content"> <!-- 6. Heading + subtext --> <h2 class="reveal text-display-sm">{heading}</h2> <!-- 7. Content grid / list --> {/* … */} </div></section>Match this skeleton, your new section reads as part of the family.
What’s next
Section titled “What’s next”- Pages — drop your new section into a new page.
- Brand colors & fonts — repaint the theme so your section adapts too.