Bottom Bar
The Bottom Bar is a custom navigation and AI control plane integrated into Nextra. It replaces the usual floating TOC UX with a two-panel drawer, custom AI workflows, and scroll controls that feel native to the RitoVision docs.
Architecture map
- BottomBar.tsx
- BottomBar.module.css
Where the TOC comes from (standard)
The docs pages already include a Nextra-generated TOC. The Bottom Bar injects itself into that flow and renders the TOC in a custom drawer instead of the default layout.
Step 1: Nextra builds the TOC per page
Each MDX page imported by Nextra returns a toc array.
Step 2: The TOC is provided to the app
docs/app/[[...slug]]/page.tsx wraps content with <TOCProvider initialTOC={toc} />.
Step 3: Bottom Bar consumes the TOC
BottomBar.tsx pulls the live toc from useTOC() and wires it into the TOC drawer.
Drawer system (shared infrastructure)
The bar uses a single drawer shell that hosts two panels: TOC and AI. The panel switch is instant, but the drawer height is measured, clamped, and animated for stability.
useDrawerControllermanages active tab, open/close state, and height measurement.- Drawer height clamps to 60% viewport height (
DRAWER_MAX_HEIGHT_RATIO) and enables scroll overflow only when needed. - The drawer closes on outside clicks or Escape with modal-priority logic (
useDrawerDismiss).
TOC drawer (custom presentation of a standard feature)
The TOC experience is standard Nextra data with custom presentation and behavior:
TOCDrawerrenders the nested list and anchors.useActiveHeadingusesIntersectionObserverto highlight the current section.- Clicking a heading uses
scrollIntoView({ behavior: 'smooth' })and closes the drawer after the jump. - Keyboard support: Enter/Space activates a TOC item via
onKeyDownhandlers.
The TOC button disables itself when a page has no headings. This prevents empty drawers and makes the bar context-aware.
AI drawer (non-standard, custom workflow)
This is the part that is not standard for Nextra: a full prompt system and AI provider launcher that lives inside the docs UI.
Prompting system (Zustand store + local persistence)
The AI prompt system is stored locally in the browser and persists across sessions.
- Store:
docs/app/stores/promptStore.tsusing Zustand +persist. - Storage key:
ai-prompts. - Default prompt: “Please explain this webpage to me”.
- Users can create, edit, delete, and select prompts in the UI.
The prompt selection modal sorts the active prompt to the top and uses a short “Prompt Activated!” animation to confirm the change.
Prompt editing flow
PromptSelectionModallists prompts and lets you activate or edit.PromptEditModalis used for create/edit and validates name + content.- Deletes are guarded with confirmation modals.
AI provider deep links
The drawer launches external AI sessions, prefilled with prompt + current page URL:
- ChatGPT:
https://chat.openai.com/?q= … - Claude:
https://claude.ai/new?q= … - Perplexity:
https://www.perplexity.ai/search?q= …
This is done via openAIDeeplink in docs/app/components/navigation/BottomBar/utils/aiDeeplinks.ts.
Copy page as markdown
The AI drawer also includes a Copy Page as Markdown action:
copyPageAsMarkdown()converts the current DOM into markdown.- Script/style/nav/header/footer are stripped before conversion.
- The result includes the page title + URL for context.
- A transient status label uses
aria-live=“polite”to announce success/failure.
Scroll controls and visibility
The right side of the bar hosts up/down scroll arrows with smart visibility:
useScrollIndicatorsshows the up arrow only when not near the top and the down arrow only when not near the bottom.- Scrolls are smooth and set a short “sticky” active state to keep the button highlighted.
- Hover/active states fade out automatically after a short timeout.
Footer-aware fade out
When the page nears the footer, the entire bar fades out so the footer stays readable.
- Computed by
FOOTER_NEAR_RATIOinsideuseScrollIndicators. BottomBar.module.cssappliesopacity: 0and disables pointer events via.fadeOut.- The fade-out is suppressed while the drawer is open to avoid interrupting interactions.
Sidebar toggle integration
The bar includes a sidebar toggle that bridges to Nextra instead of duplicating state.
useSidebarBridgefinds the native Nextra sidebar button via ARIA attributes.- A
MutationObservertracksaria-expandedto keep state in sync. - When no native button is found, it dispatches
nextra:toggleSidebar. - The button only appears at the breakpoints defined in
BottomBar.module.css(hidden on small screens, visible from 768px up).
Accessibility details
Accessibility is treated as a first-class behavior:
aria-expandedon AI and TOC toggles.aria-labelon all icon buttons and action controls.aria-liveon copy status feedback.- Keyboard controls for TOC and prompt list entries.
- Escape key closes modals first, then the drawer.
Key files
-
docs/app/components/navigation/BottomBar/BottomBar.tsx -
docs/app/components/navigation/BottomBar/drawers/AIDrawer/AIDrawer.tsx -
docs/app/components/navigation/BottomBar/drawers/TOCDrawer/TOCDrawer.tsx -
docs/app/stores/promptStore.ts -
docs/app/components/navigation/BottomBar/utils/markdownCopy.ts -
docs/app/components/navigation/BottomBar/utils/aiDeeplinks.ts