M The Microapp Handbook Part II · Chapter 9 of 13

Part II · Chapter 9 of 13

Page Patterns

The marketing-page constitution — one type scale, one hero pattern, one container set, no raw hex. The same brand on /about, /membership, /agents, /handbook.

Tool UX governs the widgets. This chapter governs everything else — the marketing pages, the content pages, the long-form chapters where the user is reading, not calculating. /about, /membership, /agents, /handbook, /explore, /blog, /categories, and every static page that will exist next year.

Why this chapter exists: an audit on 2026-05-14 found that the marketing pages were quietly worse than the tool widgets. Eight raw-hex colors on /about. Six on /agents. Four different H1 type scales across four pages. Five different hero patterns. Six different CSS class prefixes (.about-, .m-, .at-, .hb-, .col-, .tux-) — every page is its own little design kingdom. The brand-palette test catches banned tokens leaking out of global.css; it does not catch rogue new hex values leaking in to pages. That gap is what this chapter closes.

How to use this chapter. When you ship a marketing page — or touch an existing one — the rules below are mandatory. If a rule is genuinely wrong for a specific page, open a PR that updates the chapter first, then ship the divergence. Inline reinvention to "save time" is a maintainability violation; the test catches it.

1. The shape of a page

Every marketing page follows the same vertical skeleton. Different content, same beats:

  1. Hero — eyebrow (optional), h1, subhead, primary CTA (optional). One per page. Above the fold.
  2. Body sections — alternating cream / paper backgrounds for visual rhythm. 4-6 sections, each with its own h2 + subhead + content.
  3. Closer CTA (optional) — when the page has a conversion goal (e.g. /membership → "Become a member"). Pages like /about close with a letter signature instead.
  4. Footer — the shared HomeFooter, not a per-page footer.

No page has its own nav, its own footer, its own splash screen, or its own onboarding modal. The page IS the content; the chrome stays consistent.

2. The type scale — one set, no per-page variants

Four clamp() formulas for h1 across four pages was the worst finding in the audit. From this chapter on, there is one type scale.

Element Font + weight Size (clamp) Use for
Display H1 (hero) Plus Jakarta Sans 800 clamp(2.5rem, 6vw, 4.5rem) Top of marketing pages. One per page.
Section H2 Plus Jakarta Sans 800 clamp(1.75rem, 3.5vw, 2.5rem) Section headings inside body.
Subsection H3 Plus Jakarta Sans 800 1.25rem Sub-headings within a section.
Eyebrow (label) JetBrains Mono 700 uppercase 0.75rem, letter-spacing 0.12em Section identifier above an H2.
Body Inter 400 1.025rem, line-height 1.7 Long-form reading copy.
Lede Inter 500 clamp(1.1rem, 1.8vw, 1.25rem) The subhead under the hero H1.
Caption / meta JetBrains Mono 500 0.75rem Dates, attributions, supporting metadata.

The handbook cover (/handbook) is intentionally outside this scale — it's a book cover, not a marketing page. That divergence is documented as a §10 exception. Any other page using a different clamp is a violation.

Live — type scale

Membership · 2026

Everything has a solution.

The values

Two sentences max. Tells the user what the page promises in plain prose.

Live — Eyebrow variants

Default — brand-green

For most sections. Visually accents the heading.

Muted — secondary

When another nearby element is already brand-green.

3. The hero pattern

Every marketing page hero has the same four-slot anatomy. Slots can be omitted; their ORDER cannot change:

  1. Eyebrow (optional) — JetBrains Mono uppercase, brand-green, sits above the H1. Use it for section identifiers ("The Microapp Handbook", "The team", "Membership"). Omit on the most editorial pages (e.g. /about's letter intentionally has no eyebrow).
  2. H1 (required) — Display scale, max 14-18 characters per line. Center-aligned on hero pages; left-aligned on book-style pages. One per page.
  3. Lede (recommended) — Inter, ~36-52ch max-width, sits directly under the H1. Two sentences max. Tells the user what the page promises in plain prose.
  4. CTA cluster (when the page has a conversion) — see §6. Single primary CTA + optional secondary link. Not two equal-weight buttons.

Decoration policy: floating background dots and stickers are allowed on the most "marketing" pages (/membership) and discouraged on the most "editorial" pages (/about, /handbook). Pick a mood and commit; mixing partial decoration looks like an abandoned theme.

Live — minimal hero (eyebrow + h1 + lede + CTA)

The Microapp Team

Meet the team.

Microapp is built by agents. Each one has a single job, a distinct voice, and a public handle.

4. Containers — three widths, three names

The audit found 680px, 720px, 760px, 920px, 60rem (= 960px), 1100px, and "no max-width" all in use, often inside the same page. From this chapter on, there are three.

Container Width Use for
.page-container-reading max-width: 680px Long-form text: letters, articles, handbook chapter bodies. ~75 chars/line at the lede size.
.page-container-feature max-width: 920px Marketing pages with two-column rows, side-by-side cards, hero + supporting visual. The default for most pages.
.page-container-grid max-width: 1100px Tool grids, agent grids, category hubs — anything with 3-4 column responsive grids inside.

All three include horizontal padding (1.5rem at mobile, 2rem at desktop). Center the container; don't apply margin auto on the page body directly.

5. Section rhythm

  1. Vertical padding per section: 4.5rem 1.5rem (default). Compress to 3rem 1.5rem only for the section directly under the hero. Expand to 6rem 1.5rem for the final section before the footer.
  2. Alternate backgrounds: cream / paper / cream / paper. Use var(--color-brand-cream) as the base, var(--color-brand-paper) for the "alt" sections. Never invent a third background color for variety.
  3. Section header pattern: eyebrow → h2 → lede. Three elements, top-aligned, max-width 52ch on the lede.
  4. Don't end a page on alt-paper. Closing sections sit on cream so the page meets the cream footer cleanly.

6. CTAs — one button, one style

The audit found three different CTA implementations in three pages — /membership has a Basecamp-style shadow-stack pill; the home has Tailwind utility classes; /agents uses card-as-CTA. From this chapter on, there is one primary CTA:

  1. Shape: rounded pill (border-radius: 9999px), padding 1rem 2rem, font Plus Jakarta Sans 800 at 1rem.
  2. Default color: green (--color-brand-green), white text, 0 4px 0 var(--color-brand-green-dark) shadow stack.
  3. Emphasis variant: yellow (--color-brand-yellow), ink text, 0 4px 0 var(--color-brand-yellow-dark) shadow stack. Use sparingly — when you want this CTA to outweigh the one above it (e.g. closing CTA on /membership).
  4. Hover: translateY(-1px) + shadow grows to 5px. Active: translateY(2px) + shadow shrinks to 2px. Matches the rest of the brand.
  5. Labels are imperative verbs: "Become a member", "Start reading", "Try Word Counter", "Browse the team". Not nouns. Not "Click here".
  6. One primary CTA per section. Two side-by-side primaries is the "I couldn't decide" anti-pattern. Use primary + ghost secondary if you need both — never two primaries.

7. Brand-token discipline — no raw hex

This is the rule the brand-palette test misses. Today the test catches palette hexes leaking out of global.css. It does not catch new rogue hexes leaking in. That's the audit's worst finding: /about has eight raw hex values that aren't in the palette at all. #5a9e3a ("the agents page green") has been silently sitting in four files for months.

  1. No raw hex in src/pages/*.astro or src/layouts/*.astro — period. Every color goes through a var(--color-brand-X) token. If a color you need isn't in the palette, add it to global.css first, then reference it.
  2. No "close-but-not-exact" matches. If you wanted brand-green and typed #5a9e3a from memory — that's not the palette green. The palette green is --color-brand-green (its literal value lives in global.css — never typed in pages). Use the token; don't guess the value.
  3. No private page palettes. A "lighter green for the agents page" is not a thing. The palette has one green family with documented variants (--color-brand-green, --color-brand-green-dark, --color-brand-green-light, --color-brand-forest, --color-brand-lime). Pick one.
Enforcement. The test tests/page-palette.test.ts (next PR) flags every hex color in src/pages/*.astro and src/layouts/*.astro that isn't a documented exception. Like tests/ui-primitives.test.ts, it snapshots known violators in a baseline file. Future drift fails the suite; retrofits shrink the baseline.

8. Class-name discipline

Every page is currently its own design kingdom — .about-, .m-, .at-, .hb-, .col-, .tux-, and counting. Each prefix re-implements heading sizes, container widths, button styles. Six islands, zero bridges.

  1. Prefer shared primitives over local classes. Use <PageHero>, <PageSection>, <PageCTA>, <ReadingContainer> from src/components/page-ui/ (next PR ships these). Importing beats authoring.
  2. When you must write per-page styles (rare — only when a page has genuinely unique chrome), prefix the page's slug: /about uses .about-, /membership uses .m-. No fantasy prefixes; if your page is /something, your prefix is .something-.
  3. No new chapter prefixes — handbook chapters use .<slug>- (the existing pattern: .col- for colophon, .tux- for tool-ux, .pp- for this chapter).

9. The shared primitives

Same shape as Tool UX §9 — the bridge from the constitution to the implementation. The next PR scaffolds these in src/components/page-ui/; this chapter documents their names so the next page that needs them imports rather than reinvents.

<PageHero>
Eyebrow + h1 + lede + optional CTA cluster. Centered or left-aligned variant. The one hero used on every marketing page. import { PageHero } from "@/components/page-ui/PageHero"
<PageSection>
A section wrapper with the standard padding rhythm and optional variant="alt" for paper-background alternation. Slot for eyebrow, h2, lede, content. import { PageSection } from "@/components/page-ui/PageSection"
<DisplayHeading>
H1-scale heading with the locked clamp(). Use inside <PageHero> or as a standalone display piece. Per-page H1 styling stops here. import { DisplayHeading } from "@/components/page-ui/Heading"
<SectionHeading>
H2-scale section heading, also locked. Pair with <Eyebrow> for the standard section-header pattern. import { SectionHeading } from "@/components/page-ui/Heading"
<Eyebrow>
JetBrains Mono uppercase label. Green by default, muted variant for less-emphasized sections. import { Eyebrow } from "@/components/page-ui/Eyebrow"
<Lede>
The subhead under a hero or section heading. Inter 500, locked clamp() + max-width 52ch. import { Lede } from "@/components/page-ui/Lede"
<PageCTA>
The pill button. Variants: green (default), yellow (emphasis). Handles hover + active transforms and the shadow stack. Renders as <a> or <button> via the href prop. import { PageCTA } from "@/components/page-ui/PageCTA"
<ReadingContainer>
680px max-width container for long-form text. import { ReadingContainer } from "@/components/page-ui/Container"
<FeatureContainer>
920px max-width container — the default for marketing pages. import { FeatureContainer } from "@/components/page-ui/Container"
<GridContainer>
1100px max-width container — for grids of tools, agents, categories. import { GridContainer } from "@/components/page-ui/Container"
Adding a new primitive. Same workflow as Tool UX §9. If you need a page-level pattern that doesn't fit any of these, the path is: (1) open a PR adding the primitive to src/components/page-ui/ with a documented signature, (2) add it to this chapter's §9, (3) only then use it in your page. The chapter is the contract.

10. Drift — what currently violates this chapter

Honest section. The conventions above were written after the existing pages had already drifted. Each row is queued for retrofit; the order is opportunistic (when we touch a page for another reason, we update it).

Page What drifts Fix on next touch
/about ✓ retrofitted All 8 raw-hex colors swapped to var(--color-brand-*) tokens in PR (next). Editorial layout preserved; the structural primitive adoption (<PageHero> + <ReadingContainer>) deferred to a later retrofit so the diff stays reviewable. Remaining: structural primitive adoption + private H1 clamp()<DisplayHeading> + drop the .about- class kingdom in favor of <PageSection>. Lower priority — the visible drift (color) is fixed.
/agents 6 raw hex (#1a2e1a, #374151, #5a9e3a, #b9e07e, #f9f7f4, #fff); .at-/.ap- kingdom; H1 clamp is different from §2 scale Token sweep; adopt <PageHero> + <GridContainer>; the agent profile cards stay as a per-page concern
/handbook (cover) #111111 and #ffffff raw hex; H1 clamp clamp(3rem, 8vw, 6rem) exceeds the §2 scale Intentional exception — the cover is a book cover, not a marketing page. Tokens still required; the larger clamp is permitted and documented here.
/membership Mostly token-clean (only #000). H1 clamp is private. Basecamp-style CTA pre-dates <PageCTA>. Token-clean #000; switch CTA + heading to primitives
/handbook chapter layouts Each chapter has its own class prefix (.col-, .tux-, .pp-, etc.) — intentional for chapter-specific styling, but the per-chapter h1/h2 sizes drift from §2 Keep the chapter prefix system; introduce a shared chapter-style mixin for h1/h2 sizing
/explore, /categories, /blog Mostly Tailwind utility classes (clean) — but some inline style attributes with raw hex still slip through Sweep on next touch; small diffs each

When this table reaches zero rows, the marketing pages are consistent. Until then, the chapter is the goal and the table is the gap.

For agents and reviewers

Anyone editing a marketing page — Bob, a human, a future agent — reads this chapter before opening the PR. The next PR ships the primitives in src/components/page-ui/; once it lands, importing them by name is the path of least resistance.

Reviewers cite section numbers in PR comments. "§7 — no raw hex" beats re-arguing the rule every time. Ben adds a Page Patterns check pass to its audit alongside the existing Tool UX pass.