Skip to content

vercel-labs/primer-nextjs-template

Repository files navigation

primer-nextjs-template

The canonical reference for "how Primer React wires into Next.js App Router with the dark-mode surface paint contract."

Role

This project is the [example:project] source for the extract-ds-skill meta-skill. Pass this directory at Phase 1 of the meta-skill and the produced ds skill will lift the wiring in app/layout.tsx and app/globals.css verbatim into its Setup section.

Why this exists

The previous extraction run pointed the meta-skill at primer/react-template (Vite). That template's root entry wraps children in a bare <BaseStyles> with no inline backgroundColor. The meta-skill faithfully lifted that snippet — correct per its contract — and the downstream consequence is documented in ds-skill-extraction-workshop/dry-runs/2026-06-07-pivot-2/findings-dark-theme-gap.md (branch dryrun/02): a page generated against the produced skill renders Primer components dark, but the viewport behind them stays browser-default white. The Issues heading and SelectPanel trigger become invisible.

This project fixes that gap at its source. The next extraction lifts a root entry that already carries the full surface-paint contract.

The contract surface

The whole project exists to demonstrate four wiring elements that must travel together for the dark surface to extend to the viewport edges:

  1. Mode-coupling attributes on <html>. data-color-mode="auto", data-light-theme="light", data-dark-theme="dark" plus suppressHydrationWarning. These tell Primer's CSS variables which theme to resolve.
  2. <ThemeProvider colorMode="auto">. The React-side mirror of the HTML attribute. Tracks the OS preference.
  3. <BaseStyles> with inline style={{ backgroundColor: 'var(--bgColor-default)', height: '100vh' }}. Paints the descendant tree of the provider wrapper.
  4. app/globals.css painting body. Belt + suspenders — see below.

All four live in app/layout.tsx and app/globals.css. Together they are roughly 40 lines.

Belt + suspenders CSS

The inline backgroundColor on <BaseStyles> and the body { background-color: var(--bgColor-default) } rule in app/globals.css are not redundant — they paint two different layers.

  • <BaseStyles> paints the descendant <div> it renders. Anything inside that div sits on the active surface token.
  • body { background-color: … } paints the viewport itself. Anything the browser draws outside the BaseStyles wrapper — margins, scroll bounce, the page background when the wrapper is content-sized — also reads the active surface token.

Either layer alone is theoretically sufficient. Shipping both makes the wiring robust to a future Primer release that changes BaseStyles' rendering shape, and gives the meta-skill two distinct surfaces to lift (the inline style AND the CSS rule), so the next extraction cannot accidentally drop one and not the other.

Verifying the wiring

pnpm install
pnpm dev

Open http://localhost:3000. Then:

  1. Set macOS Appearance to Dark (System Settings → Appearance). The page background should be dark grey, the <PageHeader> title should be light, the body paragraph should be legible. There should be no white margins, no white body, no white scroll bounce.
  2. Switch macOS Appearance to Light. The page should flip to a light surface with dark text without a reload.
  3. Switch macOS Appearance to Auto. The page should follow the sunrise/sunset transition.

If any of those three cases shows white-on-white (or its inverse), the wiring has regressed.

Type and build checks

pnpm typecheck   # tsc --noEmit
pnpm build       # next build
pnpm verify      # both, in order

Both must exit 0 against the pinned @primer/react@38.26.0 and @primer/primitives@11.9.0 versions before the project ships as the meta-skill's source. A failure here means Primer's API has drifted in a way the lift would propagate; pin or bump deliberately, do not silently update.

What is NOT in scope

This project is wiring only. It deliberately does not ship:

  • Any product UI beyond the smoke-test <PageHeader> page.
  • A color-mode switcher component. The mode is auto; the OS picks.
  • Middleware, route handlers, API routes, server actions.
  • Tailwind, PostCSS plugins, CSS-in-JS libraries beyond Primer's own.
  • Automated visual regression testing. The smoke test above is manual.

Each of those would muddy the lift the meta-skill performs against app/layout.tsx.

Repo status

This project is its own git repository, sibling to ds-skill-extraction-workshop and ship-2025-agents-workshop-companion under ShipWorkshop/. It is registered as a workspace folder in ShipWorkshop.code-workspace.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors