Skip to content

feat(web): add mobile drawer for docs navigation#1985

Merged
ComputelessComputer merged 9 commits intomainfrom
devin/1764384364-mobile-docs-drawer
Nov 29, 2025
Merged

feat(web): add mobile drawer for docs navigation#1985
ComputelessComputer merged 9 commits intomainfrom
devin/1764384364-mobile-docs-drawer

Conversation

@ComputelessComputer
Copy link
Collaborator

@ComputelessComputer ComputelessComputer commented Nov 29, 2025

feat(web): add mobile drawer for docs navigation

Summary

Adds an expandable drawer for mobile view on documentation pages. The drawer icon (PanelLeft) only appears in the header when viewing docs pages on mobile devices.

Changes:

  • Created DocsDrawerContext to share drawer state between header and docs route
  • Added MobileDrawer component that slides in from the left with docs navigation
  • Added drawer toggle button to header that only renders on docs pages in mobile view
  • Extracted DocsNavigation component for reuse between sidebar and drawer

Updates since last revision:

  • Fixed context provider placement: moved DocsDrawerContext.Provider from docs/route.tsx to _view/route.tsx (parent layout) so the Header component can access the drawer state. This was necessary because React context only flows from ancestors to descendants.

Demo

Mobile drawer demo

View original video (rec-3273c1676b6c4178a71035b34b11b052-edited.mp4)

Review & Testing Checklist for Human

  • Test on mobile viewport: Open /docs on a mobile device or use browser dev tools to simulate mobile. Verify the drawer icon (small sidebar icon to the left of "Get reminder") appears in the header
  • Test drawer functionality: Click the drawer icon to open, verify it slides in from left. Click outside or the X button to close
  • Test navigation: Click a doc link in the drawer - verify it navigates to the correct page AND closes the drawer
  • Verify docs-only behavior: Navigate to non-docs pages (e.g., /blog, /pricing) on mobile and confirm the drawer icon does NOT appear
  • Test existing mobile menu: Ensure the hamburger menu still works correctly on all pages

Recommended test plan: Open the web app in Chrome DevTools with mobile viewport (e.g., iPhone SE 375x667), navigate to /docs, and test the full drawer flow.

Notes

  • The MobileDrawer component duplicates the docsBySection logic from LeftSidebar - this is intentional to keep the components independent, but could be refactored to share the logic if preferred
  • The drawer icon button is intentionally subtle (border-only pill with small gray icon) to match the existing header style
  • Argos visual regression is expected due to the new drawer icon in the header
  • Requested by: john@hyprnote.com (@ComputelessComputer)
  • Devin session: https://app.devin.ai/sessions/b5d49dd0eed545edb85bec8baddd2997

@devin-ai-integration
Copy link
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 29, 2025

📝 Walkthrough

Walkthrough

Adds a DocsDrawer context and provider, a mobile docs drawer (MobileDocsDrawer) with shared DocsNavigation, and a header mobile toggle that opens/closes the drawer on /docs pages; Desktop left sidebar reuses DocsNavigation.

Changes

Cohort / File(s) Summary
Context
apps/web/src/hooks/use-docs-drawer.ts
Adds DocsDrawerContext and useDocsDrawer() hook exposing { isOpen, setIsOpen } to manage drawer visibility.
Header — Mobile Trigger
apps/web/src/components/header.tsx
Adds mobile-only docs toggle button (imports PanelLeft / PanelLeftClose, uses useDocsDrawer) shown for /docs paths; toggles docsDrawer.setIsOpen(...). Also adjusts some responsive visibility classes and removes the small-screen inline logo block.
Docs Navigation Component
apps/web/src/routes/_view/docs/route.tsx
Extracts DocsNavigation to render sections/docs with currentSlug and optional onLinkClick; used by desktop LeftSidebar and mobile drawer.
App Layout & Mobile Drawer
apps/web/src/routes/_view/route.tsx
Wraps layout with DocsDrawerContext.Provider, adds isDocsDrawerOpen state, and introduces MobileDocsDrawer that builds sections (from allDocs and docsStructure), shows/hides on isOpen, and closes on backdrop or link clicks.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant Header
  participant DocsCtx as DocsDrawerContext
  participant MobileDrawer
  participant DocsNav as DocsNavigation
  participant Router

  User->>Header: Tap docs toggle (mobile)
  Header->>DocsCtx: setIsOpen(true)
  DocsCtx-->>MobileDrawer: isOpen = true (render)
  MobileDrawer->>DocsNav: render sections + currentSlug
  DocsNav-->>User: display nav links
  Note right of User: User taps a doc link
  User->>DocsNav: click link
  DocsNav->>Router: navigate to doc
  DocsNav->>DocsCtx: setIsOpen(false)
  DocsCtx-->>MobileDrawer: isOpen = false (hide)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Review provider placement and state propagation in apps/web/src/routes/_view/route.tsx
  • Verify header conditional (path startsWith '/docs') and toggle accessibility attributes
  • Confirm DocsNavigation prop contracts (onLinkClick, currentSlug) and reuse between desktop/mobile
  • Check responsive class changes and removed small-screen logo block for layout regressions

Possibly related PRs

  • chores #1635 — Also modifies apps/web/src/routes/_view/route.tsx; likely overlaps with layout/context changes.

Suggested reviewers

  • yujonglee

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 'feat(web): add mobile drawer for docs navigation' clearly and concisely summarizes the main change—adding a mobile drawer component for documentation navigation.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, covering the summary, specific changes made, context updates, demo, and testing checklist for the mobile docs drawer feature.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch devin/1764384364-mobile-docs-drawer

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

@netlify
Copy link

netlify bot commented Nov 29, 2025

Deploy Preview for hyprnote-storybook ready!

Name Link
🔨 Latest commit f4df127
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote-storybook/deploys/692a9aa69c28c700088f8611
😎 Deploy Preview https://deploy-preview-1985--hyprnote-storybook.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link

netlify bot commented Nov 29, 2025

Deploy Preview for hyprnote ready!

Name Link
🔨 Latest commit f4df127
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote/deploys/692a9aa669fd71000849decb
😎 Deploy Preview https://deploy-preview-1985--hyprnote.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@argos-ci
Copy link

argos-ci bot commented Nov 29, 2025

The latest updates on your projects. Learn more about Argos notifications ↗︎

Build Status Details Updated (UTC)
web (Inspect) ⚠️ Changes detected (Review) 3 changed Nov 29, 2025, 7:05 AM

Copy link
Contributor

@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: 2

🧹 Nitpick comments (5)
apps/web/src/hooks/use-docs-drawer.ts (1)

3-14: Context shape looks good; consider aligning setter type with React’s setState for flexibility

The context is straightforward and works, but setIsOpen is narrower than the actual setIsDrawerOpen type (Dispatch<SetStateAction<boolean>>). If you ever want to use the functional updater form (setIsOpen(prev => !prev)), the current typing will complain even though React supports it.

You could make the context future-proof and better aligned with React by typing it as:

import type { Dispatch, SetStateAction } from "react";

interface DocsDrawerContextType {
  isOpen: boolean;
  setIsOpen: Dispatch<SetStateAction<boolean>>;
}

This is purely a type-level improvement; current usage with setIsOpen(true/false) is fine.

apps/web/src/components/header.tsx (1)

192-201: Mobile docs button behavior & visibility look good; minor cleanup is possible

The mobile-only docs button is well scoped (sm:hidden container, clear aria-label, guarded by isDocsPage && docsDrawer). Once the context placement is fixed, it should behave as expected.

Small optional polish:

  • md:hidden on the button is redundant because the parent container is already sm:hidden; the button will never be visible at md and up anyway. You can safely drop md:hidden to simplify the class list.

This is cosmetic and can be deferred.

apps/web/src/routes/_view/docs/route.tsx (3)

50-86: Docs section grouping logic is duplicated between LeftSidebar and MobileDrawer

Both LeftSidebar and MobileDrawer build docsBySection with effectively identical useMemo blocks: iterating allDocs, skipping index docs, grouping by section, sorting by order, and then mapping docsStructure.sections.

This duplication:

  • Increases maintenance cost if the grouping or ordering rules change.
  • Risks subtle inconsistencies between desktop and mobile navs over time.

Consider extracting a shared helper (or hook) such as:

function useDocsSections() {
  return useMemo(() => {
    const sectionGroups: Record<
      string,
      { title: string; docs: (typeof allDocs)[0][] }
    > = {};

    allDocs.forEach((doc) => {
      if (doc.slug === "index" || doc.isIndex) return;

      const sectionName = doc.section;
      if (!sectionGroups[sectionName]) {
        sectionGroups[sectionName] = { title: sectionName, docs: [] };
      }
      sectionGroups[sectionName].docs.push(doc);
    });

    Object.values(sectionGroups).forEach((section) => {
      section.docs.sort((a, b) => a.order - b.order);
    });

    const sections = docsStructure.sections
      .map((sectionId) => {
        const sectionName =
          sectionId.charAt(0).toUpperCase() + sectionId.slice(1);
        return sectionGroups[sectionName];
      })
      .filter(Boolean);

    return { sections };
  }, []);
}

Then both components can call const { sections } = useDocsSections(); and pass sections into DocsNavigation.

This keeps behavior in sync and simplifies future changes.

Also applies to: 153-189


100-137: DocsNavigation is solid; consider cn for conditional classes per project convention

The navigation component cleanly centralizes docs link rendering and reuses it in both sidebar and drawer; the logic for currentSlug highlighting is correct.

Per the project’s guideline to use cn for conditional classNames, you might refactor:

className={`block px-3 py-1.5 text-sm rounded-sm transition-colors ${
  currentSlug === doc.slug
    ? "bg-neutral-100 text-stone-600 font-medium"
    : "text-neutral-600 hover:text-stone-600 hover:bg-neutral-50"
}`}

to:

import { cn } from "@hypr/utils";

className={cn(
  "block px-3 py-1.5 text-sm rounded-sm transition-colors",
  currentSlug === doc.slug
    ? "bg-neutral-100 text-stone-600 font-medium"
    : "text-neutral-600 hover:text-stone-600 hover:bg-neutral-50",
)}

This is stylistic, but it aligns with the shared pattern and scales better if more conditional classes are added.


139-220: Mobile drawer UX is good; add dialog semantics for better accessibility

The MobileDrawer implementation (backdrop, slide-in panel, close button, and reuse of DocsNavigation) looks solid, and the early if (!isOpen) return null; guard is clear.

For accessibility, it would be helpful to give the drawer explicit dialog semantics:

  • Add role="dialog" and aria-modal="true" on the drawer container.
  • Optionally, connect it to the “Documentation” heading with aria-labelledby.

For example:

<div
  className="fixed top-0 left-0 bottom-0 w-72 bg-white border-r border-neutral-100 shadow-lg z-50 md:hidden animate-in slide-in-from-left duration-300"
  role="dialog"
  aria-modal="true"
  aria-labelledby="docs-drawer-title"
>
  <div className="flex items-center justify-between h-[69px] px-4 border-b border-neutral-100">
    <span
      id="docs-drawer-title"
      className="font-semibold text-neutral-700"
    >
      Documentation
    </span>
    {/* close button */}
  </div>
  {/* body */}
</div>

This makes the drawer more usable with assistive technologies.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 78c9236 and 98c3893.

📒 Files selected for processing (3)
  • apps/web/src/components/header.tsx (3 hunks)
  • apps/web/src/hooks/use-docs-drawer.ts (1 hunks)
  • apps/web/src/routes/_view/docs/route.tsx (3 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.
Never do manual state management for form/mutation. Use useForm (from tanstack-form) and useQuery/useMutation (from tanstack-query) instead for 99% of cases. Avoid patterns like setError.
If there are many classNames with conditional logic, use cn (import from @hypr/utils). It is similar to clsx. Always pass an array and split by logical grouping.
Use motion/react instead of framer-motion.

Files:

  • apps/web/src/components/header.tsx
  • apps/web/src/routes/_view/docs/route.tsx
  • apps/web/src/hooks/use-docs-drawer.ts
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Agent implementations should use TypeScript and follow the established architectural patterns defined in the agent framework
Agent communication should use defined message protocols and interfaces

Files:

  • apps/web/src/hooks/use-docs-drawer.ts
🧬 Code graph analysis (2)
apps/web/src/components/header.tsx (1)
apps/web/src/hooks/use-docs-drawer.ts (1)
  • useDocsDrawer (12-14)
apps/web/src/routes/_view/docs/route.tsx (2)
apps/web/src/hooks/use-docs-drawer.ts (1)
  • DocsDrawerContext (8-10)
apps/web/src/routes/_view/docs/structure.ts (1)
  • docsStructure (1-8)
⏰ 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). (8)
  • GitHub Check: Redirect rules - hyprnote
  • GitHub Check: Header rules - hyprnote
  • GitHub Check: Pages changed - hyprnote
  • GitHub Check: Redirect rules - hyprnote-storybook
  • GitHub Check: Header rules - hyprnote-storybook
  • GitHub Check: Pages changed - hyprnote-storybook
  • GitHub Check: ci (macos, macos-14)
  • GitHub Check: fmt

Copy link
Contributor

@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: 0

🧹 Nitpick comments (3)
apps/web/src/routes/_view/route.tsx (1)

10-11: Good fix lifting DocsDrawerContext to the layout; consider memoizing the context value

Wiring DocsDrawerContext.Provider here correctly scopes the drawer state over both <Header /> and <Outlet />, which resolves the previous issue where Header could not see the drawer context from the docs route.

To avoid creating a new value object on every layout render (and thus triggering context updates even when isDocsDrawerOpen hasn’t changed), you can memoize the context value:

 function Component() {
   const router = useRouterState();
   const isDocsPage = router.location.pathname.startsWith("/docs");
   const [onTrigger, setOnTrigger] = useState<(() => void) | null>(null);
-  const [isDocsDrawerOpen, setIsDocsDrawerOpen] = useState(false);
+  const [isDocsDrawerOpen, setIsDocsDrawerOpen] = useState(false);
+  const docsDrawerValue = useMemo(
+    () => ({ isOpen: isDocsDrawerOpen, setIsOpen: setIsDocsDrawerOpen }),
+    [isDocsDrawerOpen],
+  );

   return (
     <HeroContext.Provider
       value={{
         onTrigger,
         setOnTrigger: (callback) => setOnTrigger(() => callback),
       }}
     >
-      <DocsDrawerContext.Provider
-        value={{ isOpen: isDocsDrawerOpen, setIsOpen: setIsDocsDrawerOpen }}
-      >
+      <DocsDrawerContext.Provider value={docsDrawerValue}>
         <div className="min-h-screen flex flex-col">
           <Header />
           <main className="flex-1">
             <Outlet />
           </main>
           {!isDocsPage && <Footer />}
         </div>
       </DocsDrawerContext.Provider>
     </HeroContext.Provider>
   );
 }

Also applies to: 31-42

apps/web/src/routes/_view/docs/route.tsx (2)

88-97: Nice extraction of DocsNavigation; consider minor className cleanup only if it grows

Pulling out DocsNavigation and reusing it in both the sidebar and drawer reduces duplication and keeps the section/slug logic in one place. The Link wiring (to="/docs/$" with _splat) and currentSlug highlighting look consistent with the existing matching logic.

If the conditional classes on the <Link> grow more complex in the future, it might be worth switching to the shared cn helper for readability, but the current string interpolation is still manageable.

Also applies to: 100-137


139-219: MobileDrawer behavior is solid; optional accessibility improvements

The drawer logic (early return when !isOpen, backdrop click to close, X button with aria-label, and using DocsNavigation with onLinkClick={onClose}) matches the product requirements and should give a good mobile UX. The duplicated docsBySection computation via useMemo is acceptable here given the PR note about intentional duplication.

If you want to refine accessibility later, consider:

  • Adding role="dialog" and aria-modal="true" to the drawer container.
  • Providing an aria-labelledby tying to the “Documentation” heading.
  • Optionally handling Escape key to close the drawer.

These are nice-to-haves and not blockers.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 98c3893 and bd66780.

📒 Files selected for processing (2)
  • apps/web/src/routes/_view/docs/route.tsx (2 hunks)
  • apps/web/src/routes/_view/route.tsx (3 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.
Never do manual state management for form/mutation. Use useForm (from tanstack-form) and useQuery/useMutation (from tanstack-query) instead for 99% of cases. Avoid patterns like setError.
If there are many classNames with conditional logic, use cn (import from @hypr/utils). It is similar to clsx. Always pass an array and split by logical grouping.
Use motion/react instead of framer-motion.

Files:

  • apps/web/src/routes/_view/docs/route.tsx
  • apps/web/src/routes/_view/route.tsx
🧬 Code graph analysis (1)
apps/web/src/routes/_view/route.tsx (2)
apps/web/src/hooks/use-docs-drawer.ts (1)
  • DocsDrawerContext (8-10)
apps/web/src/components/header.tsx (1)
  • Header (36-370)
⏰ 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). (5)
  • GitHub Check: Redirect rules - hyprnote
  • GitHub Check: Header rules - hyprnote
  • GitHub Check: Pages changed - hyprnote
  • GitHub Check: fmt
  • GitHub Check: ci (macos, macos-14)
🔇 Additional comments (1)
apps/web/src/routes/_view/docs/route.tsx (1)

19-37: Docs drawer context usage in docs Component looks correct

Using const docsDrawer = useDocsDrawer(); here and conditionally rendering MobileDrawer wired to docsDrawer.isOpen / docsDrawer.setIsOpen(false) lines up with the lifted provider in _view/route.tsx, so the drawer state is now shared between Header and the docs route as intended.

You could simplify slightly by assuming useDocsDrawer is non-null (if the hook already throws outside a provider) and dropping the docsDrawer && guard, but the current defensive check is fine if you prefer the looser contract.

devin-ai-integration bot and others added 9 commits November 29, 2025 15:31
- Add DocsDrawerContext to manage drawer state
- Add MobileDrawer component that shows docs navigation on mobile
- Add drawer icon (PanelLeft) to header that only appears on docs pages in mobile view
- Extract DocsNavigation component for reuse between sidebar and drawer

Co-Authored-By: john@hyprnote.com <john@hyprnote.com>
- Move the context provider to the parent layout so Header can access it
- Update docs/route.tsx to consume context instead of providing it
- This fixes the drawer icon not appearing in the header on docs pages

Co-Authored-By: john@hyprnote.com <john@hyprnote.com>
Place the logo (and optional docs drawer button) in the header prefix for small screens so the icon appears on the left side instead of the suffix. This rearranges the mobile-only markup: the docs drawer button and homepage Link are wrapped together in the prefix sm:hidden container, and the duplicate docs-button in the suffix is removed to avoid rendering the icon on the right.
Increase spacing between the logo and the drawer icon on small screens to improve visual balance and touch target separation. This adjusts the gap utility from 1 to 3 in the header component so the drawer button isn't crowded against the logo when viewing documentation pages.
Make the docs navigation toggle button available as soon as the documentation sidebar is not visible so users on smaller viewports can open the docs drawer without waiting. This change removes the sm-only wrapper around the left header block, adds a docs-drawer button (hidden on md+), and moves several nav links to be hidden on small screens (sm:block) to keep header layout consistent. This ensures the drawer toggle appears immediately when the left sidebar disappears, improving navigation for documentation pages on narrow screens.
Place the mobile documentation drawer inside the main _view route so it shares the same layout/hierarchy as the header and shows below it (header on top, drawer then body). This removes the separate drawer implementation from the docs route and reuses a MobileDocsDrawer in the top-level _view route. The change moves drawer rendering into the main layout, wires up docs data and routing for navigation links, and adjusts styles/animations to display the drawer beneath the header on mobile.

Summary of changes:
- Remove MobileDrawer component and related hook usage from apps/web/src/routes/_view/docs/route.tsx.
- Add MobileDocsDrawer, DocsNavigation, and required imports (allDocs, X icon, docsStructure, useMatchRoute, useMemo) to apps/web/src/routes/_view/route.tsx.
- Render MobileDocsDrawer conditionally under Header in the top-level _view route and manage isOpen state there.
- Build docs sections and links for the drawer using content-collections and docsStructure, preserving currentSlug highlighting and onLink close behavior.
Refine the documentation drawer to behave like a collapsible drawer with a dynamic icon and toggle behavior. Clicking the drawer icon now toggles open/closed state (and updates aria-label accordingly), and the drawer markup/CSS was adjusted for smoother transitions, larger max height, and simplified structure to remove the separate close button and header.

Changes:
- Toggle docsDrawer.isOpen when icon button is clicked and swap PanelLeft/PanelLeftClose icons.
- Update aria-label to reflect open/close state.
- Adjust drawer container classes (max-height, borders, shadow, transition) and simplify internal structure; remove explicit close button and header, increase scrollable area.
Make the mobile docs drawer slide in from the left instead of dropping down from the top, set its height to calc(100vh - 69px) so it fits beneath the header, and ensure its z-index matches/overlaps the header by using z-50 with an overlay at z-40. This improves the mobile docs UX and keeps the footer/main layout stable by moving Outlet and Footer positions.

Changes:
- Move Outlet and Footer placement inside main layout to ensure proper structure.
- Replace previous top-down collapsible block with a left sliding fixed drawer and backdrop overlay.
- Add positioning, height, width, borders, shadow, and transition classes for smooth slide animation.
- Keep DocsNavigation inside a scrollable container within the drawer.
Simplify mobile drawer UI by removing the fullscreen backdrop and embedding the drawer directly. Replaced the overlay with a drawer container that always renders and uses translate-x for show/hide, added larger shadow (shadow-2xl with neutral tint) and minor structure adjustments so navigation stays scrollable and closes on link click. This reduces DOM elements and implements the requested right-side shadow instead of a backdrop.
@ComputelessComputer ComputelessComputer force-pushed the devin/1764384364-mobile-docs-drawer branch from bd66780 to f4df127 Compare November 29, 2025 07:03
Copy link
Contributor

@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: 0

🧹 Nitpick comments (3)
apps/web/src/components/header.tsx (1)

2-8: Docs drawer toggle wiring looks correct; consider small a11y/UX refinements

  • Using isDocsPage plus the docsDrawer null-check is a good guard so the button only appears on docs routes when the context is available.
  • The toggle correctly calls docsDrawer.setIsOpen(!docsDrawer.isOpen) and swaps between PanelLeft / PanelLeftClose with a clear aria-label.

Two optional improvements:

  • Add aria-expanded={docsDrawer.isOpen} (and optionally aria-pressed) on the button so assistive tech can track the open/closed state of the drawer.
  • Consider resetting the docs drawer when the main mobile hamburger menu opens (e.g., calling docsDrawer?.setIsOpen(false) where you toggle isMenuOpen) to avoid both menus being open at once on small screens.

Also applies to: 11-11, 49-51, 59-76

apps/web/src/routes/_view/route.tsx (2)

66-117: Mobile docs drawer logic looks solid; consider tightening memo deps and slug handling

  • useMatchRoute({ to: "/docs/$", fuzzy: true }) + _splat extraction is a reasonable way to compute currentSlug for highlighting.
  • docsBySection correctly filters out index docs, groups by doc.section, sorts by order, and orders sections according to docsStructure.sections, with a safe .filter(Boolean).

A couple of small refinements you might consider:

  • Instead of an empty dependency array on useMemo, tie it to the static inputs for clarity/future‑proofing, e.g. useMemo(() => { ... }, [allDocs, docsStructure.sections]), in case those ever become dynamic.
  • If there’s ever a docs route without _splat (e.g., /docs index), currentSlug will be undefined and nothing is highlighted; that’s acceptable, but you could optionally map that case to a default slug from docsStructure.defaultPages if you want consistent highlighting.

Also applies to: 118-132


135-172: DocsNavigation is clean; optional cn and aria-current tweaks

  • The section/title rendering and currentSlug === doc.slug check for active styling are straightforward, and onLinkClick is a nice hook for closing the drawer on navigation.

Optional polish:

  • Per the coding guidelines, if this className grows, consider switching the conditional string to cn([...]) to keep class logic easier to extend.
  • For accessibility, you could add aria-current="page" on the active <Link> when currentSlug === doc.slug to give screen readers an explicit cue about the current doc.
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bd66780 and f4df127.

📒 Files selected for processing (4)
  • apps/web/src/components/header.tsx (5 hunks)
  • apps/web/src/hooks/use-docs-drawer.ts (1 hunks)
  • apps/web/src/routes/_view/docs/route.tsx (1 hunks)
  • apps/web/src/routes/_view/route.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/src/routes/_view/docs/route.tsx
  • apps/web/src/hooks/use-docs-drawer.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.
Never do manual state management for form/mutation. Use useForm (from tanstack-form) and useQuery/useMutation (from tanstack-query) instead for 99% of cases. Avoid patterns like setError.
If there are many classNames with conditional logic, use cn (import from @hypr/utils). It is similar to clsx. Always pass an array and split by logical grouping.
Use motion/react instead of framer-motion.

Files:

  • apps/web/src/components/header.tsx
  • apps/web/src/routes/_view/route.tsx
🧬 Code graph analysis (2)
apps/web/src/components/header.tsx (1)
apps/web/src/hooks/use-docs-drawer.ts (1)
  • useDocsDrawer (12-14)
apps/web/src/routes/_view/route.tsx (3)
apps/web/src/hooks/use-docs-drawer.ts (1)
  • DocsDrawerContext (8-10)
apps/web/src/components/header.tsx (1)
  • Header (42-373)
apps/web/src/routes/_view/docs/structure.ts (1)
  • docsStructure (1-8)
⏰ 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). (5)
  • GitHub Check: Redirect rules - hyprnote
  • GitHub Check: Header rules - hyprnote
  • GitHub Check: Pages changed - hyprnote
  • GitHub Check: fmt
  • GitHub Check: ci (macos, macos-14)
🔇 Additional comments (2)
apps/web/src/components/header.tsx (1)

88-88: Header nav breakpoint changes look fine; just sanity‑check layout at sm

Switching the Product dropdown container to hidden sm:block and making Blog/Pricing visible from sm upwards keeps the smallest viewport cleaner while showing more nav on small tablets and up. This looks consistent with the new docs toggle, but it’s worth a quick visual pass on 320–640px widths to ensure items don’t crowd the header.

Also applies to: 159-166

apps/web/src/routes/_view/route.tsx (1)

3-9: Lifting DocsDrawerContext.Provider into the _view layout correctly fixes Header scope

  • isDocsDrawerOpen state in Component and DocsDrawerContext.Provider around <Header />, <Outlet />, and <Footer /> ensure useDocsDrawer() is now usable from Header and any docs child routes.
  • Conditional Footer / MobileDocsDrawer rendering keyed off isDocsPage keeps the drawer docs‑only while preserving the rest of the layout.

Two minor considerations:

  • You’re deriving isDocsPage from router.location.pathname.startsWith("/docs") here and using useMatchRoute in MobileDocsDrawer; in the future you might unify both on useMatchRoute for consistency.
  • Since the provider now always wraps _view, useDocsDrawer is safe to use anywhere under this layout; it may be worth documenting this in the hook or context file.

Also applies to: 13-15, 32-37, 45-61

@ComputelessComputer ComputelessComputer merged commit 495cf0a into main Nov 29, 2025
13 of 14 checks passed
@ComputelessComputer ComputelessComputer deleted the devin/1764384364-mobile-docs-drawer branch November 29, 2025 07:10
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