# Heyz page composition template · v1

Use this brief before building a page. The live example is `/brand#composition`; tokens live in `app/design-tokens.css`.

## Page brief

- **Audience:** who needs this page?
- **Job:** one concrete thing they can do here.
- **Title:** a short, literal description of that job.
- **Context:** one or two sentences; explain the next action without internal implementation detail.
- **Primary action:** one verb and one destination or real handler.
- **Secondary action:** optional, lower emphasis.
- **Content:** the information needed to make that decision.
- **States:** loading, empty, complete, validation error, permission error, unavailable.

## Composition

1. Shared navigation and compact Portal wordmark.
2. A quiet eyebrow when it adds context; a large, left-aligned page title.
3. A readable introduction, no more than `--reading-width` (720px).
4. One primary orange action. Keep supporting links or outline buttons nearby.
5. Main content in a `--content-width` (1248px) container. Prefer one-column reading; use grids for genuinely parallel content.
6. Status or permission information next to the action it affects.
7. Shared footer. Avoid a second competing call to action.

## Visual implementation

- Import the shared `BrandMark`, `Button` and `Input`; use semantic CSS variables.
- Charcoal is the default canvas; ivory is a deliberate paper panel; orange is a focus or action cue.
- Space with the 4px scale: 8px within a control, 16px within a group, 24–32px between groups, 64–96px between major sections.
- Keep headings sentence case; Inter 400–600 for text, JetBrains Mono for code and small technical labels.
- On narrow screens, keep 20px outer gutters, stack grids and preserve reading order.

## Done means

- Every link and button has a real destination or behavior.
- Fields have persistent labels, useful help and accessible error messages.
- Tab through the page; focus is visible and never covered.
- Verify at 320px and desktop widths, 200% zoom and reduced motion.
- Check empty, error and success states; do not communicate status through color alone.
- Review the rendered page in Chromium and WebKit. Logos must keep the same Portal geometry and orange fill.
- Run `npm run brand:check` after changing brand source files.

## Copyable starting point

This server component uses the shared navigation/footer from the app layout. Copy it into a route, keep one `h1`, and replace the title, introduction, content and real destination together. The example's link opens the existing Explore page; do not ship a placeholder handler.

```tsx
import Link from "next/link";
import { Button } from "@/components/ui/button";
import styles from "./page.module.css";

export default function Page() {
  return (
    <main className={styles.page}>
      <header className={styles.introduction}>
        <p className={styles.eyebrow}>Field notes</p>
        <h1>A place for useful ideas.</h1>
        <p>Find a perspective, follow your curiosity, and make it your own.</p>
        <Button asChild size="lg">
          <Link href="/explore">Explore ideas</Link>
        </Button>
      </header>
      <section className={styles.content} aria-labelledby="starting-point">
        <h2 id="starting-point">Start with a question</h2>
        <p>What would make the next step easier to understand?</p>
      </section>
    </main>
  );
}
```

Companion `page.module.css`:

```css
.page {
  max-width: var(--content-width);
  margin-inline: auto;
  padding: 64px 48px 96px;
  color: var(--foreground);
}
.introduction,
.content {
  max-width: var(--reading-width);
}
.introduction h1 {
  font-size: clamp(40px, 6vw, 72px);
  font-weight: 500;
  line-height: 1.05;
  letter-spacing: -0.05em;
  overflow-wrap: break-word;
}
.introduction p,
.content p {
  color: var(--text-secondary);
  line-height: 1.6;
  margin-block: 24px;
}
.introduction .eyebrow {
  color: var(--brand-orange);
  font: 12px var(--font-mono);
}
.introduction a {
  min-height: 44px;
}
.content {
  margin-top: 64px;
}
.content h2 {
  font-size: 28px;
  font-weight: 500;
}
@media (max-width: 700px) {
  .page {
    padding: 40px 20px 64px;
  }
}
```

For a form, use the live `/brand#components` specimen as the companion pattern: persistent label, connected help, `aria-invalid`, an error message and an announced result. Keep a real loading/empty/error state next to the relevant content as soon as the page depends on data.
