Skip to content

Refactor/separate docs components pages#29

Merged
pras75299 merged 6 commits into
mainfrom
refactor/separate-docs-components-pages
Apr 15, 2026
Merged

Refactor/separate docs components pages#29
pras75299 merged 6 commits into
mainfrom
refactor/separate-docs-components-pages

Conversation

@pras75299
Copy link
Copy Markdown
Owner

@pras75299 pras75299 commented Apr 15, 2026

PR Title

fix(www): resolve theme hydration mismatch in app layout

Type of Change

  • 🚀 New Component
  • 🐛 Bug Fix
  • 📝 Documentation Update
  • 💄 Styling or Animation adjustment
  • 🔧 CLI Tooling
  • 🧹 Refactoring / Chore

Description

Fixed a hydration mismatch caused by theme initialization diverging between server and client renders.
The previous theme state logic could render "dark" on the server but "light" on initial client hydration (from localStorage), which caused React to regenerate the tree on the client.

What changed

  • Updated apps/www/contexts/theme-context.tsx to use a hydration-safe theme synchronization approach with useSyncExternalStore.
  • Ensured server snapshot and first client hydration snapshot are consistent.
  • Applied persisted theme after mount, then synced DOM (data-theme) and subscribers.
  • Preserved theme persistence and cross-tab updates.

This removes the hydration warning and keeps theme behavior consistent.

Testing

  • Ran app lint checks:
    • cd apps/www && pnpm lint
  • Ran production build:
    • cd apps/www && pnpm build
  • Manually verified:
    • Theme toggle still switches between light/dark.
    • No hydration mismatch error on /components render path.

Checklist (for New Components)

If adding a new component, please ensure you've completed the following:

  • Created registry/{component-name}.tsx with the source code.
  • Added entry to registry/config.ts specifying dependencies and Tailwind configuration.
  • Ran pnpm build:registry to regenerate registry.json.
  • Added a showcase demo in apps/www/app/page.tsx.
  • Formatted the code using Prettier / project standards.
  • Verified animations use motion (Motion.dev).
  • Verified the component supports standard Tailwind classes via className prop (cn utility).

Related Issues

Fixes hydration mismatch on /components caused by SSR/client theme state divergence.

Summary by CodeRabbit

Release Notes

  • New Features

    • Navigation restructured: section navigation consolidated into fixed top header bar.
    • Documentation pages now redirect to components section for streamlined access.
  • Improvements

    • Enhanced theme state management for improved persistence and consistency.
    • Simplified component animations for better performance.

Simplify docs layout and slug page structure, align components chrome
with wider max width and tighter padding, and extend Next config as needed.

Made-with: Cursor
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 15, 2026

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

Project Deployment Actions Updated (UTC)
uniqueui-platform Ready Ready Preview, Comment Apr 15, 2026 4:47pm

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 15, 2026

Warning

Rate limit exceeded

@pras75299 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 46 minutes and 1 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 46 minutes and 1 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 129a8b79-96d6-4f29-a505-41b3171ef600

📥 Commits

Reviewing files that changed from the base of the PR and between 885fec4 and a132691.

📒 Files selected for processing (3)
  • apps/www/app/components/layout.tsx
  • apps/www/app/components/page.tsx
  • apps/www/contexts/theme-context.tsx
📝 Walkthrough

Walkthrough

The application's navigation and layout architecture is restructured to consolidate the separate docs and components sections. Docs routes now redirect to components. A unified horizontal navigation header (SECTION_NAV) replaces scattered navigation patterns across layouts. Theme state management is refactored to use external store synchronization. Detail-page sidebars transition to a fixed component list structure.

Changes

Cohort / File(s) Summary
Layout Restructuring
apps/www/app/components/layout.tsx, apps/www/app/docs/layout.tsx, apps/www/app/templates/layout.tsx
Removed activeSection computation and detail-page section nav UI. Introduced shared topHeader fragment with SECTION_NAV horizontal nav, ThemeToggle, and mobile sidebar toggle. Replaced mobile header/back structure with fixed sidebar for components list. Updated container styling from flex-based with padding offsets to responsive max-w-[1600px] layouts. Removed scroll-spy, hash tracking, and IntersectionObserver logic from docs layout.
Routing and Navigation Consolidation
apps/www/app/docs/[slug]/page.tsx, apps/www/next.config.ts
Changed docs page from rendering full documentation to performing server-side redirect to /components/${slug}. Removed doc metadata imports, code highlighting, and props tables. Added permanent next.config.ts redirect rule for /docs/:slug/components/:slug.
Animation Simplification
apps/www/app/components/page.tsx
Removed AnimatePresence wrapper and per-card motion properties (layout, exit animations). Added key={activeCategory + search} to force grid remounting on filter changes. Simplified motion behavior to enter/animate-only transitions.
Theme State Management
apps/www/contexts/theme-context.tsx
Replaced useState-based theme with useSyncExternalStore external store. Introduced module-level theme cache with hydration tracking and storage event listeners. Added getSnapshot/getServerSnapshot for SSR-consistent initial theme ("dark"), deferred to cache after hydration.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • PR #5: Modifies apps/www/app/components/layout.tsx sidebar rendering—complements this PR's fixed sidebar introduction by altering how grouped components are categorized and displayed within it.
  • PR #23: Modifies apps/www/app/layout.tsx with font loading and hydration flag adjustments—related through shared root layout configuration patterns affecting the navigation and theme infrastructure.

Poem

🐰 The layout hops to one bright place,
No docs-path left to chase,
A header shared, a sidebar neat,
Theme stores in time sync'd sweet,
Navigation flows as one, complete! ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title 'Refactor/separate docs components pages' does not match the actual primary change, which is fixing a theme hydration mismatch via useSyncExternalStore in theme-context.tsx. Update the PR title to reflect the main change: 'fix(www): resolve theme hydration mismatch in app layout' or similar, as described in the PR description.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The PR description is mostly complete with proper sections (Type of Change, Description, Testing, Related Issues) and clearly explains the hydration fix, testing performed, and related changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/separate-docs-components-pages

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
Copy Markdown

@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 (1)
apps/www/app/templates/layout.tsx (1)

62-83: ⚠️ Potential issue | 🟡 Minor

Expose the active section in the templates nav.

The active link here is only styled visually. Adding aria-current="page" keeps this header consistent with the docs/components layouts and gives screen readers the current-page cue.

Suggested fix
                 <Link
                   key={href}
                   href={href}
+                  aria-current={isActive ? "page" : undefined}
                   className={cn(
                     "relative flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-sm font-medium transition-colors",

As per coding guidelines, "Include standard HTML / ARIA attributes where applicable for accessibility."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/www/app/templates/layout.tsx` around lines 62 - 83, The nav Link
currently only indicates active state visually; update the Link element to
expose the active section to assistive tech by adding an ARIA attribute—set
aria-current="page" when isActive (e.g., aria-current={isActive ? "page" :
undefined} on the Link). Locate the Link component block that renders the
motion.span with layoutId="templatesNavPill" and Icon/label children and add the
conditional aria-current attribute without changing the visual behavior or
existing motion.span/layoutId.
🧹 Nitpick comments (2)
apps/www/app/docs/[slug]/page.tsx (1)

4-14: Drop one of the two /docs/:slug redirect layers.

apps/www/next.config.ts already redirects /docs/:slug to /components/:slug before this route is resolved, so keeping this page means extra route/build surface and two redirect definitions to maintain. If the config redirect is the intended permanent behavior, I’d remove this route file entirely.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/www/app/docs/`[slug]/page.tsx around lines 4 - 14, This page defines a
second redirect layer for /docs/:slug (functions generateStaticParams,
dynamicParams and the DocSlugRedirect component calling redirect) which
duplicates the redirect already in apps/www/next.config.ts; remove this route
file (apps/www/app/docs/[slug]/page.tsx) so only the config-based redirect
remains, or alternatively remove the config redirect and keep this file — but do
not keep both; delete the file containing
generateStaticParams/dynamicParams/DocSlugRedirect to drop the duplicate
redirect.
apps/www/app/components/layout.tsx (1)

37-133: Consider extracting the shared section header into one component.

This header markup now exists in the components, docs, and templates layouts with only small variations, so nav/theme/accessibility changes will keep drifting between files. Centralizing it would make this refactor easier to maintain.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/www/app/components/layout.tsx` around lines 37 - 133, The topHeader JSX
is duplicated across layouts — extract it into a single component (e.g.,
SharedTopHeader or TopHeader) that receives the needed props (isDark, pathname,
isOverview, isMobileMenuOpen, setIsMobileMenuOpen) and renders the same
structure using SECTION_NAV, motion, ThemeToggle, ArrowLeft, Menu, X, etc.;
replace the local topHeader variable with a call to this new component and
export it for reuse in components, docs, and templates so future
nav/theme/accessibility changes are made in one place.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/www/app/components/layout.tsx`:
- Around line 117-128: The mobile menu toggle button is missing accessible
attributes; update the button rendered in the component (the element using
setIsMobileMenuOpen, isMobileMenuOpen, Menu and X) to include a descriptive
aria-label (e.g., "Toggle component list"), aria-expanded={isMobileMenuOpen} to
reflect state, and aria-controls pointing to the sidebar's id (add an id like
"component-sidebar" to the sidebar container if not present) so assistive tech
knows the relationship; apply the same attributes to the other toggle instance
referenced in the file (the block near lines 177-187).

In `@apps/www/app/components/page.tsx`:
- Around line 197-202: The filter count element rendered in motion.p
(key={`${search}-${activeCategory}`}) isn't announced to assistive tech; make it
a live region by adding ARIA attributes: set aria-live="polite",
aria-atomic="true" and role="status" on that motion.p (or wrap the count in an
element with those attributes) so screen readers reliably announce updates when
search or activeCategory changes.

In `@apps/www/contexts/theme-context.tsx`:
- Around line 36-42: The storage event handler onStorage updates themeCache and
calls onStoreChange but doesn't update the DOM theme attribute; modify onStorage
to, after validating s === "light" || s === "dark", set
document.documentElement.dataset.theme = s in addition to assigning themeCache
and calling onStoreChange so the receiving tab's CSS (selectors using
[data-theme]) updates immediately; ensure you reference STORAGE_KEY and the
onStorage handler (and keep existing themeCache and onStoreChange behavior).

---

Outside diff comments:
In `@apps/www/app/templates/layout.tsx`:
- Around line 62-83: The nav Link currently only indicates active state
visually; update the Link element to expose the active section to assistive tech
by adding an ARIA attribute—set aria-current="page" when isActive (e.g.,
aria-current={isActive ? "page" : undefined} on the Link). Locate the Link
component block that renders the motion.span with layoutId="templatesNavPill"
and Icon/label children and add the conditional aria-current attribute without
changing the visual behavior or existing motion.span/layoutId.

---

Nitpick comments:
In `@apps/www/app/components/layout.tsx`:
- Around line 37-133: The topHeader JSX is duplicated across layouts — extract
it into a single component (e.g., SharedTopHeader or TopHeader) that receives
the needed props (isDark, pathname, isOverview, isMobileMenuOpen,
setIsMobileMenuOpen) and renders the same structure using SECTION_NAV, motion,
ThemeToggle, ArrowLeft, Menu, X, etc.; replace the local topHeader variable with
a call to this new component and export it for reuse in components, docs, and
templates so future nav/theme/accessibility changes are made in one place.

In `@apps/www/app/docs/`[slug]/page.tsx:
- Around line 4-14: This page defines a second redirect layer for /docs/:slug
(functions generateStaticParams, dynamicParams and the DocSlugRedirect component
calling redirect) which duplicates the redirect already in
apps/www/next.config.ts; remove this route file
(apps/www/app/docs/[slug]/page.tsx) so only the config-based redirect remains,
or alternatively remove the config redirect and keep this file — but do not keep
both; delete the file containing
generateStaticParams/dynamicParams/DocSlugRedirect to drop the duplicate
redirect.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9a7c493b-6871-4047-91c3-62a89970aab0

📥 Commits

Reviewing files that changed from the base of the PR and between 0efafcd and 885fec4.

📒 Files selected for processing (7)
  • apps/www/app/components/layout.tsx
  • apps/www/app/components/page.tsx
  • apps/www/app/docs/[slug]/page.tsx
  • apps/www/app/docs/layout.tsx
  • apps/www/app/templates/layout.tsx
  • apps/www/contexts/theme-context.tsx
  • apps/www/next.config.ts

Comment thread apps/www/app/components/layout.tsx
Comment thread apps/www/app/components/page.tsx
Comment thread apps/www/contexts/theme-context.tsx Outdated
Comment on lines +36 to +42
const onStorage = (e: StorageEvent) => {
if (e.key !== STORAGE_KEY) return;
try {
const s = localStorage.getItem(STORAGE_KEY) as Theme | null;
if (s === "light" || s === "dark") {
themeCache = s;
onStoreChange();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Sync data-theme inside the storage listener.

The cross-tab path updates themeCache and re-renders subscribers, but it never updates document.documentElement.dataset.theme in the receiving tab. Any CSS driven by [data-theme] stays stale until the user toggles locally or reloads.

Suggested fix
       const s = localStorage.getItem(STORAGE_KEY) as Theme | null;
       if (s === "light" || s === "dark") {
         themeCache = s;
-        onStoreChange();
+        document.documentElement.dataset.theme = s;
+        emit();
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/www/contexts/theme-context.tsx` around lines 36 - 42, The storage event
handler onStorage updates themeCache and calls onStoreChange but doesn't update
the DOM theme attribute; modify onStorage to, after validating s === "light" ||
s === "dark", set document.documentElement.dataset.theme = s in addition to
assigning themeCache and calling onStoreChange so the receiving tab's CSS
(selectors using [data-theme]) updates immediately; ensure you reference
STORAGE_KEY and the onStorage handler (and keep existing themeCache and
onStoreChange behavior).

@pras75299 pras75299 merged commit acfc959 into main Apr 15, 2026
3 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request May 27, 2026
16 tasks
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