Skip to content

Editorial page

A publication front page moving from lead article to progressively denser indexes.

Contentstartereditorialpublicationmagazineindex

Live preview

Open the full page
full widthLive preview — open it in a new tab for the full-height version.
Open the preview in a new tab

Source

This exact file renders the preview above.

import EditorialNavbar from '@/components/blocks/navigation/editorial'
import EditorialHeader from '@/components/blocks/headers/editorial'
import FeaturedPlusListBlog from '@/components/blocks/sections/blog/featured-plus-list'
import CategoryColumnsBlog from '@/components/blocks/sections/blog/category-columns'
import EditorialIndexBlog from '@/components/blocks/sections/blog/editorial-index'
import LogosWithQuote from '@/components/blocks/sections/logos/with-quote'
import NewsletterWithArchive from '@/components/blocks/sections/newsletter/with-archive'
import EditorialFooter from '@/components/blocks/footers/editorial'

/**
 * Editorial page
 *
 * A publication front page: masthead, statement, lead article, then progressively
 * denser indexes. The subscribe block sits near the bottom, after the reader
 * has had a chance to decide whether they want more.
 */
export default function EditorialPattern() {
  return (
    <>
      <EditorialNavbar />
      <main>
        <EditorialHeader />
        <FeaturedPlusListBlog />
        <CategoryColumnsBlog />
        <EditorialIndexBlog />
        <LogosWithQuote />
        <NewsletterWithArchive />
      </main>
      <EditorialFooter />
    </>
  )
}

components/patterns/editorial.tsx

import EditorialNavbar from '@/components/blocks/navigation/editorial'
import EditorialHeader from '@/components/blocks/headers/editorial'
import FeaturedPlusListBlog from '@/components/blocks/sections/blog/featured-plus-list'
import CategoryColumnsBlog from '@/components/blocks/sections/blog/category-columns'
import EditorialIndexBlog from '@/components/blocks/sections/blog/editorial-index'
import LogosWithQuote from '@/components/blocks/sections/logos/with-quote'
import NewsletterWithArchive from '@/components/blocks/sections/newsletter/with-archive'
import EditorialFooter from '@/components/blocks/footers/editorial'

/**
 * Editorial page
 *
 * A publication front page: masthead, statement, lead article, then progressively
 * denser indexes. The subscribe block sits near the bottom, after the reader
 * has had a chance to decide whether they want more.
 */
export default function EditorialPattern() {
  return (
    <>
      <EditorialNavbar />
      <main>
        <EditorialHeader />
        <FeaturedPlusListBlog />
        <CategoryColumnsBlog />
        <EditorialIndexBlog />
        <LogosWithQuote />
        <NewsletterWithArchive />
      </main>
      <EditorialFooter />
    </>
  )
}

components/blocks/headers/editorial.tsx

import Link from 'next/link'
import { ArrowRight } from 'lucide-react'
import { ButtonLink } from '@/components/ui/button'
import { Container } from '@/components/ui/layout'

/**
 * Editorial header
 *
 * A hero built like a title page: a rule, an eyebrow, an oversized serif
 * statement and a standfirst set at the reading measure. No image, no card,
 * no gradient — the typography carries the whole thing, which is why the
 * display size steps three times between 390px and 1440px.
 */
export default function EditorialHeader() {
  return (
    <header className="border-b border-line bg-canvas py-section">
      <Container>
        <div className="border-t-2 border-ink pt-6">
          <p className="label-caps text-ink-subtle">Foundry 1.0 · March 2026</p>
          <h1 className="display-type mt-6 max-w-4xl text-4xl leading-[1.05] font-semibold text-ink-strong sm:text-5xl lg:text-6xl">
            A frontend system from which many products can be built.
          </h1>
          <p className="mt-6 max-w-[46rem] text-md leading-relaxed text-ink-muted sm:text-lg">
            Primitives, navigation, forms, sections, page patterns and complete starter products —
            all composed from one token system, documented against their own source.
          </p>
          <div className="mt-8 flex flex-wrap items-center gap-3">
            <ButtonLink
              href="/components"
              size="lg"
              trailingIcon={<ArrowRight className="size-4" />}
            >
              Browse the catalogue
            </ButtonLink>
            <Link
              href="/docs/getting-started"
              className="text-sm font-medium text-ink underline underline-offset-4 hover:text-accent"
            >
              Read the getting-started guide
            </Link>
          </div>
        </div>
      </Container>
    </header>
  )
}

components/blocks/sections/blog/featured-plus-list.tsx

import Link from 'next/link'
import { Container } from '@/components/ui/layout'
import { Badge } from '@/components/ui/badge'
import { formatShortDate } from '@/lib/format'
import { posts } from '@/content/demo'

/**
 * Featured post with a list
 *
 * One lead article plus a scannable list. The asymmetry does the editorial
 * work: a uniform grid of six posts tells a reader nothing about which one the
 * editor thinks matters.
 */
export default function FeaturedPlusListBlog() {
  const [lead, ...rest] = posts

  return (
    <section className="border-b border-line bg-canvas py-section">
      <Container>
        <div className="flex flex-wrap items-end justify-between gap-4">
          <h2 className="display-type text-2xl font-semibold text-ink-strong sm:text-3xl">
            Writing
          </h2>
          <Link
            href="/starters/blog/preview/stories"
            className="text-sm font-semibold text-accent underline underline-offset-4"
          >
            All articles
          </Link>
        </div>

        <div className="mt-10 grid gap-10 lg:grid-cols-[1.3fr_1fr] lg:gap-16">
          {lead ? (
            <article>
              <Link href={`/starters/blog/preview/stories/${lead.slug}`} className="group block">
                <div
                  className="grid-paper aspect-[16/9] w-full rounded-xl border border-line bg-surface-sunken"
                  aria-hidden="true"
                />
                <div className="mt-5 flex flex-wrap items-center gap-2">
                  <Badge tone="accent">{lead.category}</Badge>
                  <time dateTime={lead.date} className="text-xs text-ink-subtle">
                    {formatShortDate(lead.date)}
                  </time>
                  <span className="text-xs text-ink-subtle">· {lead.readingTime}</span>
                </div>
                <h3 className="display-type mt-3 text-xl leading-snug font-semibold text-ink-strong group-hover:text-accent sm:text-2xl">
                  {lead.title}
                </h3>
                <p className="mt-2 text-sm leading-relaxed text-ink-muted">{lead.excerpt}</p>
                <p className="mt-3 text-xs text-ink-subtle">{lead.author}</p>
              </Link>
            </article>
          ) : null}

          <ul className="divide-y divide-[var(--color-border-subtle)] border-t border-line">
            {rest.slice(0, 5).map((post) => (
              <li key={post.slug}>
                <Link
                  href={`/starters/blog/preview/stories/${post.slug}`}
                  className="group block py-5"
                >
                  <div className="flex flex-wrap items-center gap-2">
                    <span className="label-caps text-accent">{post.category}</span>
                    <time dateTime={post.date} className="text-xs text-ink-subtle">
                      {formatShortDate(post.date)}
                    </time>
                  </div>
                  <h3 className="mt-1.5 text-md font-semibold text-ink-strong group-hover:text-accent">
                    {post.title}
                  </h3>
                  <p className="mt-1 text-sm text-ink-muted">{post.excerpt}</p>
                </Link>
              </li>
            ))}
          </ul>
        </div>
      </Container>
    </section>
  )
}

Demo source — adapt to your project. Foundry is not published as a package.

Usage

The subscribe block sits near the bottom, after the reader has had a chance to decide whether they want more — and it shows back issues rather than asserting quality.

  • Density should increase down the page: lead, list, columns, contents.
  • A colophon footer is part of the form for a publication, not decoration.

Variants and states

Every entry below is a genuine difference in behaviour or layout, and every one of them is visible in the preview above.

  • Masthead with scrolling sections
  • Lead article plus list
  • Category columns and numbered contents
  • Archive-backed subscribe block

Accessibility

Article semantics
Every entry is an article element with its own heading.
Time elements
All dates carry a machine-readable datetime attribute.

Foundry implements published ARIA patterns and is tested against them. No WCAG certification is claimed — see the accessibility documentation for what is and is not covered.