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:
- Mode-coupling attributes on
<html>.data-color-mode="auto",data-light-theme="light",data-dark-theme="dark"plussuppressHydrationWarning. These tell Primer's CSS variables which theme to resolve. <ThemeProvider colorMode="auto">. The React-side mirror of the HTML attribute. Tracks the OS preference.<BaseStyles>with inlinestyle={{ backgroundColor: 'var(--bgColor-default)', height: '100vh' }}. Paints the descendant tree of the provider wrapper.app/globals.csspaintingbody. 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 devOpen http://localhost:3000. Then:
- 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. - Switch macOS Appearance to Light. The page should flip to a light surface with dark text without a reload.
- 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 orderBoth 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.