feat: adopt shadcn/ui default theme system#12
Conversation
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>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Summary by CodeRabbitRelease Notes
✏️ Tip: You can customize this high-level summary in your review settings. WalkthroughThis 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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: ASSERTIVE Plan: Pro ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (1)
🧰 Additional context used🧠 Learnings (3)📚 Learning: 2025-11-27T09:57:56.171ZApplied to files:
📚 Learning: 2025-11-25T18:37:08.183ZApplied to files:
📚 Learning: 2025-11-24T20:44:21.076ZApplied to files:
🪛 LanguageToolREADME.md[grammar] ~229-~229: Make sure that the noun ‘setup’ is correct. Did you mean the past participle “set up”? (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)
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. Comment |
There was a problem hiding this comment.
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 justifybaseline-browser-mapping— no direct usage detected
baseline-browser-mappingappears 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-10button andsize-5icon 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")orsetTheme("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
⛔ Files ignored due to path filters (4)
package-lock.jsonis excluded by!**/package-lock.jsonx_docs/images/explained.jpgis excluded by!**/*.jpgx_docs/images/github-social-thin.jpgis excluded by!**/*.jpgx_docs/images/rough.jpgis 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 manualuseMemoanduseCallbackunless profiling shows performance is needed
Files:
lib/formatPrice.tslib/formatPrice.test.tse2e/homepage.spec.tsapp/fonts.tscomponents/theme-toggle.tsxcomponents/button.tsxapp/layout.tsxapp/page.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Always use
@/import aliases for all imports, including sibling files (e.g.,@/app/fontsinstead of./fonts)
Files:
lib/formatPrice.tslib/formatPrice.test.tse2e/homepage.spec.tsapp/fonts.tscomponents/theme-toggle.tsxcomponents/button.tsxapp/layout.tsxapp/page.tsx
**/*.{css,postcss}
📄 CodeRabbit inference engine (CLAUDE.md)
Tailwind v4 uses
@import "tailwindcss"syntax in CSS files (not@tailwinddirectives)
Files:
app/globals.cssx_docs/reference/default.theme.cssx_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.mdx_docs/own/WORDS.mdCLAUDE.mdapp/layout.tsxapp/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.mdx_docs/own/WORDS.mdCLAUDE.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.mdx_docs/reference/README.mdCLAUDE.mdapp/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.mdCLAUDE.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.mdCLAUDE.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.mdapp/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-scopedThe added
lint:md:*,test:e2e:*, andtest:unit:*permissions line up with existing npm scripts and stay constrained tonpm 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 modelThe new main/header/article structure, semantic headings, and use of
text-foreground/text-muted-foregroundall 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 clearlyThe added Tailkits link,
shadcn-custom-themeCLI 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@themereference looks consistent and self‑containedThe 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 expectationsThis 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 consistentThe 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 conventionsThe additions for React Compiler, CI/CD tooling, coding practices, Tailwind v4 import syntax, and Promise-based
paramsgive 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 tokensThe Montserrat/Lora/JetBrains Mono setup is straightforward and lines up with the
--font-*variables used inapp/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–compatibleThe dark variant, OKLCH semantic tokens,
@theme inlinemappings (including the new--font-*and--radius-*tokens), and@layer basetypography/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 behaviourThe Vitest suite exercises typical price values (zero, decimals, large numbers) against
formatPriceand 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 descriptionThe
formatPriceutility cleanly wrapsIntl.NumberFormatfor GBP withen-GBlocale, 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 exampleYou 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 systemThe updated
baseclasses (wider md width andfont-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 systemImporting the three font configs and applying their
.variableclasses on<html>, plus themin-h-screen bg-background antialiasedbody and centredmax-w-3xlcontainer, 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-themesversion thatdefaultTheme="system"andenableSystemare the intended configuration for your target behaviour.
| 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/); |
There was a problem hiding this comment.
🧹 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
| (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). |
There was a problem hiding this comment.
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.
| (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.”
| // 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) | ||
| } |
There was a problem hiding this comment.
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
createDemoStorehere and move thepersist/skipHydrationlogic 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.
| // 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>
Summary
darktosystem(respects OS preference)Test plan
npm run checkpassesnpm run test:unitpasses (4 tests)npm run test:e2epasses (2 tests)Also includes minor docs reorganisation and CLAUDE.md updates.
🤖 Generated with Claude Code