The canonical reference for "how Primer React wires into Next.js App Router with the dark-mode surface paint contract."
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.
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 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.
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.
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.
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.
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.
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.