Skip to content

feat: adopt shadcn/ui default theme system#12

Merged
michellepace merged 5 commits intomainfrom
feat/ui-library-ready-theme
Dec 1, 2025
Merged

feat: adopt shadcn/ui default theme system#12
michellepace merged 5 commits intomainfrom
feat/ui-library-ready-theme

Conversation

@michellepace
Copy link
Owner

Summary

  • Replace custom theme with shadcn/ui's default semantic colour variables (OKLCH)
  • Add base typography styles (h1-h6 hierarchy, inline code, code blocks)
  • Add radius tokens (sm/md/lg/xl)
  • Switch fonts to Montserrat (display) + Lora (body) + JetBrains Mono
  • Change default theme from dark to system (respects OS preference)
  • Remove Counter demo component

Test plan

  • npm run check passes
  • npm run test:unit passes (4 tests)
  • npm run test:e2e passes (2 tests)
  • Theme toggle works in light/dark/system modes

Also includes minor docs reorganisation and CLAUDE.md updates.

🤖 Generated with Claude Code

michellepace and others added 4 commits December 1, 2025 14:39
Theme (globals.css):
- Replace custom theme with shadcn/ui's default semantic colour variables
- Add base typography styles (h1-h6 hierarchy, inline code, code blocks)
- Add radius tokens (sm/md/lg/xl) for consistent border radius

Page content (page.tsx):
- Add guidance on choosing between shadcn/ui and Tailwind Plus
- Explain how to theme components with semantic variables vs palette overrides
- Include advanced example of palette remapping technique

Fonts:
- Switch to Montserrat (display) + Lora (body) + JetBrains Mono

Behaviour:
- Change default theme from "dark" to "system" (respects OS preference)
- Remove Counter demo component and its tests

Template now uses shadcn's exact variable naming, so installed components inherit
the theme automatically.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Replace wildcard patterns with specific command prefixes. Wildcards like
`test:*` don't match `test:unit` — they require content after the asterisk.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Structure:
- Rename xdocs/ to x_docs/ for consistency
- Add reference/ with Tailwind default theme CSS and preflight styles
- Add own/ for project-specific notes (theming, zustand, terminology)
- Move existing docs into appropriate subfolders

Reference files provide baseline CSS for comparison when customising themes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Add Lefthook, GitHub Actions, Vercel to tech stack
- Add Breaking Changes section with Next.js 15 async params example
- Add vercel list command reference
- Update database recommendations (Neon, Supabase, Convex)

Clearer guidance on tooling and common gotchas for new contributors.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@vercel
Copy link

vercel bot commented Dec 1, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
nextjs-base Ready Ready Preview Comment Dec 1, 2025 11:24am

@coderabbitai
Copy link

coderabbitai bot commented Dec 1, 2025

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced comprehensive dark theme system with unified CSS variable architecture for consistent styling across light and dark modes.
  • Style

    • Updated typography with new font selections (Lora and Montserrat).
    • Enhanced button and theme toggle component layouts for improved alignment.
  • Removed

    • Counter component and related functionality removed from homepage.
  • Documentation

    • Added guides covering Next.js routing concepts, Zustand state management patterns, and theme resource recommendations.
    • Streamlined project setup documentation with updated asset paths.
  • Chores

    • Updated build tool permissions and theme provider defaults to system preference detection.

✏️ Tip: You can customize this high-level summary in your review settings.

Walkthrough

This PR overhauls theming (fonts, global CSS variables, dark variant, Tailwind mappings), switches ThemeProvider to system default, removes the Counter component and its tests, adjusts UI component styles, simplifies e2e tests, adds documentation (Next.js, Zustand, Tailwind references), and updates some configuration and dev-dependencies.

Changes

Cohort / File(s) Summary
Configuration & Permissions
.claude/settings.json
Adjusted allowed Bash commands: Bash(npm run lint:*)Bash(npm run lint:md:*), Bash(npm run test:*)Bash(npm run test:e2e:*), and added Bash(npm run test:unit:*).
Top-level Docs
CLAUDE.md, README.md
CLAUDE.md: added React Compiler, CI/CD tools, Lefthook, breaking-changes notes and reorganised content. README.md: updated image paths, condensed setup/deploy instructions and linked to x_docs/project-setup.md.
Fonts
app/fonts.ts
Replaced Geist and Space_Grotesk with Montserrat and Lora; renamed export jetBrainsMonojetbrainsMono; updated CSS variable names.
Global Theme & Styling
app/globals.css
Large theming refactor: introduced root CSS custom properties, .dark variant, @custom-variant dark, Tailwind @theme mappings to --color-* aliases, font/radius tokens, and concrete base-layer element styles (typography, code blocks, borders, resets).
Layout
app/layout.tsx
Updated font usage to new exports, changed ThemeProvider from defaultTheme="dark"defaultTheme="system" with enableSystem, applied min-h-screen bg-background, and wrapped children in a centred container (mx-auto max-w-3xl px-8 py-8).
Homepage
app/page.tsx
Removed Counter import and component usage; replaced with new header/article layout and updated content.
Components — Styling
components/button.tsx, components/theme-toggle.tsx
button.tsx: md:w-[158px]md:w-[170px], added font-display. theme-toggle.tsx: adjusted Button to inline-flex items-center justify-center, reduced SunIcon size.
Components — Removal
components/counter.tsx, components/counter.test.tsx
Deleted the Counter component and its unit test file (increment/decrement and pricing tests removed).
Tests
e2e/homepage.spec.ts, lib/formatPrice.test.ts
homepage.spec.ts: consolidated tests into a single "homepage loads successfully" title check. formatPrice.test.ts: added Vitest scaffold/demo header.
Utilities
lib/formatPrice.ts
Added a demo header comment only; implementation unchanged.
Dependencies
package.json
Added devDependency baseline-browser-mapping ^2.8.32.
Docs — Guides & References
x_docs/own/WORDS.md, x_docs/own/zustand.md, x_docs/own/theme_resources.md
New Next.js guide (WORDS.md), comprehensive Zustand/persist guide (zustand.md), and Tailwind theme resources plus shadcn-custom-theme notes (theme_resources.md).
Docs — Reference Assets
x_docs/reference/README.md, x_docs/reference/default.theme.css, x_docs/reference/preflight.css
Added reference section and two new CSS files: default.theme.css (design tokens, animations) and preflight.css (reset/preflight rules).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • Focus review on app/globals.css (heavy, centralised token mapping, dark variant, Tailwind aliases).
  • Verify app/layout.tsx ThemeProvider changes and container/wrapper styling effects.
  • Ensure removal of components/counter.tsx has no remaining imports/usages.
  • Review new reference CSS files (x_docs/reference/*) for accuracy and inadvertent overrides.

Possibly related PRs

Poem

🐰
I hop through variables, fonts anew,
Montserrat strides and Lora hums through,
The Counter vanished, docs bloom instead,
Dark and light dance where CSS led,
A rabbit cheers — deploy and review!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarises the main change: adopting shadcn/ui's default theme system, which is the primary objective and encompasses the colour variables, typography, radius tokens, and font changes.
Description check ✅ Passed The description is directly related to the changeset, providing a clear summary of the key modifications including theme replacement, typography, fonts, and component removal, with a test plan demonstrating verification.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/ui-library-ready-theme

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 62ba000 and 8a3a9e6.

⛔ Files ignored due to path filters (1)
  • x_docs/images/app_screenshot.jpg is excluded by !**/*.jpg
📒 Files selected for processing (1)
  • README.md (4 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-11-27T09:57:56.171Z
Learnt from: michellepace
Repo: michellepace/nextjs-base PR: 6
File: app/page.tsx:21-22
Timestamp: 2025-11-27T09:57:56.171Z
Learning: In the nextjs-base repository, the maintainer prefers to keep placeholder/development text like "hello (h4)" and "no ui kit or icon library yet" in app/page.tsx rather than requiring production-ready content before merging.

Applied to files:

  • README.md
📚 Learning: 2025-11-25T18:37:08.183Z
Learnt from: michellepace
Repo: michellepace/nextjs-base PR: 2
File: app/layout.tsx:76-77
Timestamp: 2025-11-25T18:37:08.183Z
Learning: The vercel/analytics and vercel/speed-insights packages have built-in environment detection in "auto" mode by default. They automatically detect development vs production environments using NODE_ENV and similar framework variables, and only send analytics data in production. They no-op during local development, so manual NODE_ENV checks are redundant.

Applied to files:

  • README.md
📚 Learning: 2025-11-24T20:44:21.076Z
Learnt from: michellepace
Repo: michellepace/nextjs-base PR: 1
File: evals/actions.md:0-0
Timestamp: 2025-11-24T20:44:21.076Z
Learning: The `evals/` directory in the nextjs-base repository contains personal documentation and notes, not production code, so it doesn't require the same rigor as workflow files (e.g., placeholder references like `@<SHA>` are acceptable).

Applied to files:

  • README.md
🪛 LanguageTool
README.md

[grammar] ~229-~229: Make sure that the noun ‘setup’ is correct. Did you mean the past participle “set up”?
Context: ...Hub - A branch protection ruleset to be setup to protect main. Incldues checks for Gi...

(BE_VB_OR_NN)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Run E2E Tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
package.json (1)

32-45: Remove or justify baseline-browser-mapping — no direct usage detected

baseline-browser-mapping appears only in package.json and package-lock.json with no references in source code, tests, or npm scripts. It has a CLI interface but isn't invoked anywhere in the project. If it's a transitive dependency of another package, it shouldn't be listed as a direct devDependency. Either remove it or clarify its purpose.

components/theme-toggle.tsx (1)

48-56: Toggle sizing looks good; consider whether users should be able to return to “system”

The new inline-flex size-10 button and size-5 icon give a clearer, better-aligned control — nice improvement.

Given the app now defaults to a system theme, note that this handler still only ever calls setTheme("light") or setTheme("dark"), so once a user clicks it there’s no way back to a system-driven theme from the UI. If three-way (light/dark/system) control is desired, you may want to extend the toggle logic or add a separate “system” option; if a simple two-state toggle is intentional, this is fine as-is.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f266561 and 62ba000.

⛔ Files ignored due to path filters (4)
  • package-lock.json is excluded by !**/package-lock.json
  • x_docs/images/explained.jpg is excluded by !**/*.jpg
  • x_docs/images/github-social-thin.jpg is excluded by !**/*.jpg
  • x_docs/images/rough.jpg is excluded by !**/*.jpg
📒 Files selected for processing (21)
  • .claude/settings.json (1 hunks)
  • CLAUDE.md (3 hunks)
  • README.md (1 hunks)
  • app/fonts.ts (1 hunks)
  • app/globals.css (1 hunks)
  • app/layout.tsx (2 hunks)
  • app/page.tsx (1 hunks)
  • components/button.tsx (1 hunks)
  • components/counter.test.tsx (0 hunks)
  • components/counter.tsx (0 hunks)
  • components/theme-toggle.tsx (1 hunks)
  • e2e/homepage.spec.ts (1 hunks)
  • lib/formatPrice.test.ts (1 hunks)
  • lib/formatPrice.ts (1 hunks)
  • package.json (1 hunks)
  • x_docs/own/WORDS.md (1 hunks)
  • x_docs/own/theme_resources.md (3 hunks)
  • x_docs/own/zustand.md (1 hunks)
  • x_docs/reference/README.md (1 hunks)
  • x_docs/reference/default.theme.css (1 hunks)
  • x_docs/reference/preflight.css (1 hunks)
💤 Files with no reviewable changes (2)
  • components/counter.test.tsx
  • components/counter.tsx
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Write TypeScript with strict mode enabled
Only add "use client" directive when interactivity is needed; Server Components are the default in Next.js App Router
Avoid manual useMemo and useCallback unless profiling shows performance is needed

Files:

  • lib/formatPrice.ts
  • lib/formatPrice.test.ts
  • e2e/homepage.spec.ts
  • app/fonts.ts
  • components/theme-toggle.tsx
  • components/button.tsx
  • app/layout.tsx
  • app/page.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Always use @/ import aliases for all imports, including sibling files (e.g., @/app/fonts instead of ./fonts)

Files:

  • lib/formatPrice.ts
  • lib/formatPrice.test.ts
  • e2e/homepage.spec.ts
  • app/fonts.ts
  • components/theme-toggle.tsx
  • components/button.tsx
  • app/layout.tsx
  • app/page.tsx
**/*.{css,postcss}

📄 CodeRabbit inference engine (CLAUDE.md)

Tailwind v4 uses @import "tailwindcss" syntax in CSS files (not @tailwind directives)

Files:

  • app/globals.css
  • x_docs/reference/default.theme.css
  • x_docs/reference/preflight.css
🧠 Learnings (7)
📓 Common learnings
Learnt from: michellepace
Repo: michellepace/nextjs-base PR: 6
File: app/page.tsx:21-22
Timestamp: 2025-11-27T09:57:56.171Z
Learning: In the nextjs-base repository, the maintainer prefers to keep placeholder/development text like "hello (h4)" and "no ui kit or icon library yet" in app/page.tsx rather than requiring production-ready content before merging.
📚 Learning: 2025-11-27T09:57:56.171Z
Learnt from: michellepace
Repo: michellepace/nextjs-base PR: 6
File: app/page.tsx:21-22
Timestamp: 2025-11-27T09:57:56.171Z
Learning: In the nextjs-base repository, the maintainer prefers to keep placeholder/development text like "hello (h4)" and "no ui kit or icon library yet" in app/page.tsx rather than requiring production-ready content before merging.

Applied to files:

  • README.md
  • x_docs/own/WORDS.md
  • CLAUDE.md
  • app/layout.tsx
  • app/page.tsx
📚 Learning: 2025-11-24T20:44:21.076Z
Learnt from: michellepace
Repo: michellepace/nextjs-base PR: 1
File: evals/actions.md:0-0
Timestamp: 2025-11-24T20:44:21.076Z
Learning: The `evals/` directory in the nextjs-base repository contains personal documentation and notes, not production code, so it doesn't require the same rigor as workflow files (e.g., placeholder references like `@<SHA>` are acceptable).

Applied to files:

  • README.md
  • x_docs/own/WORDS.md
  • CLAUDE.md
📚 Learning: 2025-11-27T10:10:31.478Z
Learnt from: CR
Repo: michellepace/nextjs-base PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-27T10:10:31.478Z
Learning: Applies to **/*.{css,postcss} : Tailwind v4 uses `import "tailwindcss"` syntax in CSS files (not `tailwind` directives)

Applied to files:

  • x_docs/own/theme_resources.md
  • x_docs/reference/README.md
  • CLAUDE.md
  • app/globals.css
📚 Learning: 2025-11-27T10:10:31.478Z
Learnt from: CR
Repo: michellepace/nextjs-base PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-27T10:10:31.478Z
Learning: Applies to **/*.{ts,tsx} : Only add `"use client"` directive when interactivity is needed; Server Components are the default in Next.js App Router

Applied to files:

  • x_docs/own/WORDS.md
  • CLAUDE.md
📚 Learning: 2025-11-27T10:10:31.478Z
Learnt from: CR
Repo: michellepace/nextjs-base PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-27T10:10:31.478Z
Learning: Use Next.js 16 App Router (not Pages Router) for routing

Applied to files:

  • x_docs/own/WORDS.md
  • CLAUDE.md
📚 Learning: 2025-11-27T10:10:31.478Z
Learnt from: CR
Repo: michellepace/nextjs-base PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-27T10:10:31.478Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Always use `@/` import aliases for all imports, including sibling files (e.g., `@/app/fonts` instead of `./fonts`)

Applied to files:

  • CLAUDE.md
  • app/fonts.ts
🧬 Code graph analysis (1)
app/layout.tsx (2)
app/fonts.ts (3)
  • montserrat (3-7)
  • lora (9-13)
  • jetbrainsMono (15-19)
components/theme-provider.tsx (1)
  • ThemeProvider (6-11)
🪛 LanguageTool
README.md

[grammar] ~222-~222: Make sure that the noun ‘setup’ is correct. Did you mean the past participle “set up”?
Context: ...Hub - A branch protection ruleset to be setup to protect main. Incldues checks for Gi...

(BE_VB_OR_NN)

x_docs/own/WORDS.md

[uncategorized] ~5-~5: Possible missing article found.
Context: ... Next.js Words ## Client Component If component requires browser interactivity, such as...

(AI_HYDRA_LEO_MISSING_THE)


[formatting] ~68-~68: Insert a comma before quoting reported speech: “says, "”…
Context: ...rams: Promise<{ id: string }>`: Next.js says "I promise I'll give you this data later,...

(SAID_COMMA_SPEECH)

x_docs/reference/README.md

[style] ~9-~9: Would you like to use the Oxford spelling “customize”? The spelling ‘customise’ is also correct.
Context: ...ws, and decide when to use defaults vs. customise. preflight.css: - Ta...

(OXFORD_SPELLING_Z_NOT_S)


[style] ~13-~13: Would you like to use the Oxford spelling “normalizes”? The spelling ‘normalises’ is also correct.
Context: ...ss): - Tailwind's CSS reset layer that normalises browser defaults and fixes cross-browse...

(OXFORD_SPELLING_Z_NOT_S)


[style] ~20-~20: Would you like to use the Oxford spelling “customize”? The spelling ‘customise’ is also correct.
Context: ...csslines 5-246 | Pick from palette or customise | | "What text sizes exist?" |default...

(OXFORD_SPELLING_Z_NOT_S)

CLAUDE.md

[style] ~9-~9: Would you like to use the Oxford spelling “optimizations”? The spelling ‘optimisations’ is also correct.
Context: ...React Compiler* enabled for automatic optimisations - TypeScript 5 with strict mode - *...

(OXFORD_SPELLING_Z_NOT_S)


[grammar] ~32-~32: Did you mean “to Avoid”?
Context: ...client"when interactivity is needed - Avoid manualuseMemo/useCallback` unless p...

(MISSING_TO_BEFORE_A_VERB)

x_docs/own/zustand.md

[duplication] ~14-~14: Possible typo: you repeated a word.
Context: ...tion and page refreshes within the same tab 3. Tab isolation - Each browser tab maintains ...

(ENGLISH_WORD_REPEAT_RULE)


[uncategorized] ~23-~23: Possible missing comma found.
Context: ...nge than a database. Whilst at the same time you can "play with the application." Wh...

(AI_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~647-~647: ‘In’ is unnecessary in most cases before the expression ‘next .’.
Context: ...ateful demo/sandbox development pattern in Next.js 16. Your approach enables rapid prot...

(CONFUSION_OF_NN_IN_NEXT_NNP)

🔇 Additional comments (14)
.claude/settings.json (1)

36-45: Claude permissions for new npm scripts look well-scoped

The added lint:md:*, test:e2e:*, and test:unit:* permissions line up with existing npm scripts and stay constrained to npm run …, which keeps the surface area relatively tight while giving Claude access to the intended tooling.

app/page.tsx (1)

7-118: Homepage rewrite aligns well with the new theming model

The new main/header/article structure, semantic headings, and use of text-foreground / text-muted-foreground all fit the shadcn-style theme and Tailwind v4 setup. Nothing here looks problematic from a Next.js, React, or accessibility perspective for a template page.

x_docs/own/theme_resources.md (1)

5-135: New theming resources and CLI example read clearly

The added Tailkits link, shadcn-custom-theme CLI snippet, and the premium marketplace row slot neatly into the existing structure and make the theming docs more actionable without introducing duplication.

x_docs/reference/default.theme.css (1)

1-403: Comprehensive @theme reference looks consistent and self‑contained

The palette, typography, spacing, radius, shadow, and animation tokens form a coherent Tailwind v4‑style default theme reference. As a doc/reference asset under x_docs/, this is a solid, copy‑pastable baseline without any obvious inconsistencies.

x_docs/reference/preflight.css (1)

1-405: Preflight/reset stylesheet matches modern Tailwind v4 expectations

This preflight closely mirrors Tailwind’s modern reset: normalised box‑sizing, typography, lists, tables, media, and form controls with well‑documented cross‑browser fixes. As a reference file it is coherent and should integrate cleanly with the theme tokens.

x_docs/reference/README.md (1)

1-22: Reference doc is clear and consistent

The Tailwind reference pointers and quick table are concise and accurate, and the British spelling is consistent with the repo’s stated convention. No changes needed.

CLAUDE.md (1)

9-56: Guidance updates read well and align with repo conventions

The additions for React Compiler, CI/CD tooling, coding practices, Tailwind v4 import syntax, and Promise-based params give clear, opinionated guidance and match the rest of the repo’s conventions (British English, App Router, @/ aliases). No changes needed from a code-practices point of view.

app/fonts.ts (1)

1-19: Font configuration matches the new theme tokens

The Montserrat/Lora/JetBrains Mono setup is straightforward and lines up with the --font-* variables used in app/globals.css. This should integrate cleanly with the display/serif/mono mappings.

app/globals.css (1)

3-179: Theming and base styles are well-structured and Tailwind v4–compatible

The dark variant, OKLCH semantic tokens, @theme inline mappings (including the new --font-* and --radius-* tokens), and @layer base typography/code styles form a coherent theme system and follow the Tailwind v4 import/variant model. This should work smoothly with shadcn/ui and the updated fonts.

lib/formatPrice.test.ts (1)

1-25: Unit tests cover the expected formatting behaviour

The Vitest suite exercises typical price values (zero, decimals, large numbers) against formatPrice and uses the @/ alias as per project guidelines. This is a solid, low-noise demo test for CI/CD.

lib/formatPrice.ts (1)

2-13: Price formatter implementation matches tests and description

The formatPrice utility cleanly wraps Intl.NumberFormat for GBP with en-GB locale, exactly matching what the demo tests assert. The new header comment makes its purpose in the template explicit.

x_docs/own/WORDS.md (1)

15-16: Tidy up duplicated dynamic route example

You list /users/[userid] twice in the examples; consider either removing one or swapping it for a different path (e.g. /profile/[userid]) to avoid readers thinking there’s a subtle difference.

⛔ Skipped due to learnings
Learnt from: michellepace
Repo: michellepace/nextjs-base PR: 1
File: evals/actions.md:0-0
Timestamp: 2025-11-24T20:44:21.076Z
Learning: The `evals/` directory in the nextjs-base repository contains personal documentation and notes, not production code, so it doesn't require the same rigor as workflow files (e.g., placeholder references like `@<SHA>` are acceptable).
components/button.tsx (1)

16-21: Button base classes align with the new typography system

The updated base classes (wider md width and font-display) look consistent with the new display font and should improve visual balance without affecting behaviour.

app/layout.tsx (1)

5-32: Root layout font and theme setup looks consistent with the new design system

Importing the three font configs and applying their .variable classes on <html>, plus the min-h-screen bg-background antialiased body and centred max-w-3xl container, all line up cleanly with the new typography and token-based theming. The ThemeProvider props also match the move to a system-based default.

Please just confirm against your next-themes version that defaultTheme="system" and enableSystem are the intended configuration for your target behaviour.

Comment on lines +3 to +5
test("homepage loads successfully", async ({ page }) => {
await page.goto("/");

// Locate the Documentation link
const docLink = page.getByRole("link", { name: "Documentation" });

// Verify link is visible
await expect(docLink).toBeVisible();

// Verify correct href (points to Next.js docs)
await expect(docLink).toHaveAttribute("href", /^https:\/\/nextjs\.org\/docs/);

// Verify opens in new tab (security best practice)
await expect(docLink).toHaveAttribute("target", "_blank");

// Verify has security attributes
await expect(docLink).toHaveAttribute("rel", "noopener noreferrer");
});

// Test the counter
test("counter buttons", async ({ page }) => {
await page.goto("/");

// Initial state should be 0.
await expect(page.getByText("Quantity: 0")).toBeVisible();

// Click the increment button.
await page.getByRole("button", { name: "+" }).click();

// Expects the quantity to update to 1.
await expect(page.getByText("Quantity: 1")).toBeVisible();

// Click the decrement button.
await page.getByRole("button", { name: "-" }).click();

// Expects the quantity to return to 0.
await expect(page.getByText("Quantity: 0")).toBeVisible();
await expect(page).toHaveTitle(/Next/);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider asserting homepage content tied to this template, not just the title

At the moment the e2e spec only checks that the page title matches /Next/, which is quite generic and may change independently of the actual homepage content.

You could make this test more robust (and aligned with the new copy) by also asserting for a key text node or landmark on the page, for example:

await expect(page.getByRole("heading", { name: "Choose a UI Library" })).toBeVisible();

This keeps the test lightweight while ensuring the primary guidance content remains present.

🤖 Prompt for AI Agents
In e2e/homepage.spec.ts around lines 3-5, the test only asserts the page title
which is too generic; update the test to also assert a stable piece of homepage
content by selecting a specific visible landmark or text (for example a main
heading like "Choose a UI Library") using an accessible selector (getByRole or
getByText) and call toBeVisible()/toHaveText() so the spec verifies the key
guidance copy in addition to the title.

README.md Outdated
Comment on lines 222 to 224
(3) GitHub - A branch protection ruleset to be setup to protect main. Incldues checks for GitHub workflow jobs to pass before merging PR to main. See [xdocs/project-setup.md](xdocs/project-setup.md).

```markdown
# Go Do This In GitHub on Repo

Create GitHub Branch Ruleset:
- name: "Protect main branch"
- enforced status: Active
- target branch: include default branch (main)
- Rules:
- Restrict deletions
- Require a pull request before merging
- Allowed merge methods: Merge (only)
- Require status checks to pass 🔥
- Require branches to be up to date before merging
- Status Checks that are required
- Search to add "Run Lint & Type Checks" job
- Search to add "Run Unit Tests" job
- Search to add "Run E2E Tests" job
- Block force pushes
```

(4) Vercel For Deploys

```text
1. Sign in → New Project → connect to this repo → deploy it
2. Check Vercel Speed Insights and Web Analytics are enabled (see `layout.tsx`)
3. [Optional] GitHub → Branch ruleset → Add "Vercel" status check
- This requires Vercel deployment to succeed before merge (separate from E2E tests)
4. E2E tests on Vercel Preview deployments:
- Vercel auto-triggers `test-e2e-vercel.yml` via repository_dispatch on each Preview deploy
- To bypass Deployment Protection, create the bypass secret:
- Vercel → Project Settings → Deployment Protection → Protection Bypass for Automation → Add Secret
- GitHub → Repository Settings → Secrets → Actions → New repository secret:
Name: VERCEL_AUTOMATION_BYPASS_SECRET
Value: (the secret from Vercel)
```
(4) Vercel For Deploys - When you raise a PR it automatically deploys to Vercel Preview and Playwright e2e tests run on that too in addition to GitHub servers. When you merge the PR into main, you are deploying to Vercel prod. See [xdocs/project-setup.md](xdocs/project-setup.md).
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix minor typos in branch protection note

Tiny documentation nits here: “setup” should be “set up” and “Incldues” is misspelt.

Apply this diff to tidy it up:

-(3) GitHub - A branch protection ruleset to be setup to protect main. Incldues checks for GitHub workflow jobs to pass before merging PR to main. See [xdocs/project-setup.md](xdocs/project-setup.md).
+(3) GitHub - A branch protection ruleset to be set up to protect main. Includes checks for GitHub workflow jobs to pass before merging PR to main. See [xdocs/project-setup.md](xdocs/project-setup.md).
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
(3) GitHub - A branch protection ruleset to be setup to protect main. Incldues checks for GitHub workflow jobs to pass before merging PR to main. See [xdocs/project-setup.md](xdocs/project-setup.md).
```markdown
# Go Do This In GitHub on Repo
Create GitHub Branch Ruleset:
- name: "Protect main branch"
- enforced status: Active
- target branch: include default branch (main)
- Rules:
- Restrict deletions
- Require a pull request before merging
- Allowed merge methods: Merge (only)
- Require status checks to pass 🔥
- Require branches to be up to date before merging
- Status Checks that are required
- Search to add "Run Lint & Type Checks" job
- Search to add "Run Unit Tests" job
- Search to add "Run E2E Tests" job
- Block force pushes
```
(4) Vercel For Deploys
```text
1. Sign in → New Project → connect to this repo → deploy it
2. Check Vercel Speed Insights and Web Analytics are enabled (see `layout.tsx`)
3. [Optional] GitHub → Branch ruleset → Add "Vercel" status check
- This requires Vercel deployment to succeed before merge (separate from E2E tests)
4. E2E tests on Vercel Preview deployments:
- Vercel auto-triggers `test-e2e-vercel.yml` via repository_dispatch on each Preview deploy
- To bypass Deployment Protection, create the bypass secret:
- Vercel → Project Settings → Deployment Protection → Protection Bypass for Automation → Add Secret
- GitHub → Repository Settings → Secrets → Actions → New repository secret:
Name: VERCEL_AUTOMATION_BYPASS_SECRET
Value: (the secret from Vercel)
```
(4) Vercel For Deploys - When you raise a PR it automatically deploys to Vercel Preview and Playwright e2e tests run on that too in addition to GitHub servers. When you merge the PR into main, you are deploying to Vercel prod. See [xdocs/project-setup.md](xdocs/project-setup.md).
(3) GitHub - A branch protection ruleset to be set up to protect main. Includes checks for GitHub workflow jobs to pass before merging PR to main. See [xdocs/project-setup.md](xdocs/project-setup.md).
(4) Vercel For Deploys - When you raise a PR it automatically deploys to Vercel Preview and Playwright e2e tests run on that too in addition to GitHub servers. When you merge the PR into main, you are deploying to Vercel prod. See [xdocs/project-setup.md](xdocs/project-setup.md).
🧰 Tools
🪛 LanguageTool

[grammar] ~222-~222: Make sure that the noun ‘setup’ is correct. Did you mean the past participle “set up”?
Context: ...Hub - A branch protection ruleset to be setup to protect main. Incldues checks for Gi...

(BE_VB_OR_NN)

🤖 Prompt for AI Agents
In README.md around lines 222 to 224, there are minor typos in the branch
protection note: change “setup” to the verb phrase “set up” and correct
“Incldues” to “Includes”; update the sentence so it reads something like “A
branch protection ruleset to be set up to protect main. Includes checks for
GitHub workflow jobs to pass before merging PR to main.”

Comment on lines +308 to +371
// src/providers/demo-store-provider.tsx
'use client'

import { type ReactNode, createContext, useRef, useContext, useEffect } from 'react'
import { useStore } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'

import { type DemoStore, createDemoStore, defaultInitState } from '@/stores/demo-store'

export type DemoStoreApi = ReturnType<typeof createDemoStore>

export const DemoStoreContext = createContext<DemoStoreApi | undefined>(
undefined,
)

export const DemoStoreProvider = ({
children,
}: {
children: ReactNode
}) => {
const storeRef = useRef<DemoStoreApi | null>(null)

if (storeRef.current === null) {
// Create store with persist middleware
storeRef.current = createStore<DemoStore>()(
persist(
(set) => ({
...defaultInitState,
// ... your actions here
}),
{
name: 'demo-storage',
storage: createJSONStorage(() => sessionStorage),
skipHydration: true, // Important for Next.js SSR
},
),
)
}

// Rehydrate on client
useEffect(() => {
if (storeRef.current) {
storeRef.current.persist.rehydrate()
}
}, [])

return (
<DemoStoreContext.Provider value={storeRef.current}>
{children}
</DemoStoreContext.Provider>
)
}

export const useDemoStore = <T,>(
selector: (store: DemoStore) => T,
): T => {
const demoStoreContext = useContext(DemoStoreContext)

if (!demoStoreContext) {
throw new Error(`useDemoStore must be used within DemoStoreProvider`)
}

return useStore(demoStoreContext, selector)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add missing createStore import and consider reusing the factory

In the “Create Context Provider” snippet you call createStore<DemoStore>() but never import createStore, so this example will not compile as written. In addition, you’re bypassing the earlier createDemoStore factory even though DemoStoreApi is defined from it, which slightly undermines the “store factory” pattern you just introduced.

I’d suggest:

  • Add the missing import to the snippet:
-import { type ReactNode, createContext, useRef, useContext, useEffect } from 'react'
-import { useStore } from 'zustand'
+import { type ReactNode, createContext, useRef, useContext, useEffect } from 'react'
+import { useStore } from 'zustand'
+import { createStore } from 'zustand/vanilla'
  • Or, alternatively, reuse createDemoStore here and move the persist/skipHydration logic into that factory so there’s a single source of truth for store creation.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// src/providers/demo-store-provider.tsx
'use client'
import { type ReactNode, createContext, useRef, useContext, useEffect } from 'react'
import { useStore } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'
import { type DemoStore, createDemoStore, defaultInitState } from '@/stores/demo-store'
export type DemoStoreApi = ReturnType<typeof createDemoStore>
export const DemoStoreContext = createContext<DemoStoreApi | undefined>(
undefined,
)
export const DemoStoreProvider = ({
children,
}: {
children: ReactNode
}) => {
const storeRef = useRef<DemoStoreApi | null>(null)
if (storeRef.current === null) {
// Create store with persist middleware
storeRef.current = createStore<DemoStore>()(
persist(
(set) => ({
...defaultInitState,
// ... your actions here
}),
{
name: 'demo-storage',
storage: createJSONStorage(() => sessionStorage),
skipHydration: true, // Important for Next.js SSR
},
),
)
}
// Rehydrate on client
useEffect(() => {
if (storeRef.current) {
storeRef.current.persist.rehydrate()
}
}, [])
return (
<DemoStoreContext.Provider value={storeRef.current}>
{children}
</DemoStoreContext.Provider>
)
}
export const useDemoStore = <T,>(
selector: (store: DemoStore) => T,
): T => {
const demoStoreContext = useContext(DemoStoreContext)
if (!demoStoreContext) {
throw new Error(`useDemoStore must be used within DemoStoreProvider`)
}
return useStore(demoStoreContext, selector)
}
// src/providers/demo-store-provider.tsx
'use client'
import { type ReactNode, createContext, useRef, useContext, useEffect } from 'react'
import { useStore } from 'zustand'
import { createStore } from 'zustand/vanilla'
import { persist, createJSONStorage } from 'zustand/middleware'
import { type DemoStore, createDemoStore, defaultInitState } from '@/stores/demo-store'
export type DemoStoreApi = ReturnType<typeof createDemoStore>
export const DemoStoreContext = createContext<DemoStoreApi | undefined>(
undefined,
)
export const DemoStoreProvider = ({
children,
}: {
children: ReactNode
}) => {
const storeRef = useRef<DemoStoreApi | null>(null)
if (storeRef.current === null) {
// Create store with persist middleware
storeRef.current = createStore<DemoStore>()(
persist(
(set) => ({
...defaultInitState,
// ... your actions here
}),
{
name: 'demo-storage',
storage: createJSONStorage(() => sessionStorage),
skipHydration: true, // Important for Next.js SSR
},
),
)
}
// Rehydrate on client
useEffect(() => {
if (storeRef.current) {
storeRef.current.persist.rehydrate()
}
}, [])
return (
<DemoStoreContext.Provider value={storeRef.current}>
{children}
</DemoStoreContext.Provider>
)
}
export const useDemoStore = <T,>(
selector: (store: DemoStore) => T,
): T => {
const demoStoreContext = useContext(DemoStoreContext)
if (!demoStoreContext) {
throw new Error(`useDemoStore must be used within DemoStoreProvider`)
}
return useStore(demoStoreContext, selector)
}
🤖 Prompt for AI Agents
In x_docs/own/zustand.md around lines 308 to 371, the provider snippet calls
createStore<DemoStore>() but never imports createStore and it also bypasses the
existing createDemoStore factory; fix by either adding the missing import from
'zustand' (import { createStore } from 'zustand') so the snippet compiles, or
(preferred) refactor to reuse createDemoStore: move the
persist/skipHydration/createJSONStorage logic into createDemoStore (or make
createDemoStore accept a persistence option) so the provider simply calls that
factory and assigns its return to storeRef.current, keeping a single source of
truth for store creation and preserving the DemoStoreApi type.

- Add app screenshot showing light/dark modes
- Fix image link path and open in new tab
- Correct caption text (was referencing wrong project)
- Concise alt text for accessibility

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@michellepace michellepace merged commit 9d3416d into main Dec 1, 2025
7 checks passed
@michellepace michellepace deleted the feat/ui-library-ready-theme branch December 1, 2025 11:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant