33 BookOpen ,
44 Building2 ,
55 ChevronDown ,
6+ ChevronLeft ,
7+ ChevronRight ,
68 ChevronUp ,
79 FileText ,
810 History ,
@@ -20,6 +22,7 @@ import { useEffect, useRef, useState } from "react";
2022import { cn } from "@hypr/utils" ;
2123
2224import { SearchTrigger } from "@/components/search" ;
25+ import { useBlogToc } from "@/hooks/use-blog-toc" ;
2326import { useDocsDrawer } from "@/hooks/use-docs-drawer" ;
2427import { useHandbookDrawer } from "@/hooks/use-handbook-drawer" ;
2528import { getPlatformCTA , usePlatform } from "@/hooks/use-platform" ;
@@ -88,8 +91,12 @@ export function Header() {
8891 const isDocsPage = router . location . pathname . startsWith ( "/docs" ) ;
8992 const isHandbookPage =
9093 router . location . pathname . startsWith ( "/company-handbook" ) ;
94+ const isBlogArticlePage =
95+ router . location . pathname . startsWith ( "/blog/" ) &&
96+ router . location . pathname !== "/blog/" ;
9197 const docsDrawer = useDocsDrawer ( ) ;
9298 const handbookDrawer = useHandbookDrawer ( ) ;
99+ const blogToc = useBlogToc ( ) ;
93100 const lastScrollY = useRef ( 0 ) ;
94101
95102 useEffect ( ( ) => {
@@ -161,14 +168,19 @@ export function Header() {
161168 < SearchTrigger variant = "mobile" />
162169 </ div >
163170 ) }
171+ { isBlogArticlePage && blogToc && blogToc . toc . length > 0 && (
172+ < BlogTocSubBar blogToc = { blogToc } maxWidthClass = { maxWidthClass } />
173+ ) }
164174 </ header >
165175
166176 { /* Spacer to account for fixed header */ }
167177 < div
168178 className = {
169179 isDocsPage || isHandbookPage
170180 ? "h-17.25 md:h-17.25 max-md:h-[calc(69px+52px)]"
171- : "h-17.25"
181+ : isBlogArticlePage && blogToc && blogToc . toc . length > 0
182+ ? "h-[calc(69px+44px)] sm:h-17.25"
183+ : "h-17.25"
172184 }
173185 />
174186
@@ -220,7 +232,7 @@ function LeftNav({
220232 < Logo />
221233 < Link
222234 to = "/why-hyprnote/"
223- className = "hidden sm :block text-sm text-neutral-600 hover:text-neutral-800 transition-all hover:underline decoration-dotted"
235+ className = "hidden md :block text-sm text-neutral-600 hover:text-neutral-800 transition-all hover:underline decoration-dotted"
224236 >
225237 Why Hyprnote
226238 </ Link >
@@ -788,6 +800,71 @@ function MobileSolutionsList({
788800 ) ;
789801}
790802
803+ function BlogTocSubBar ( {
804+ blogToc,
805+ maxWidthClass,
806+ } : {
807+ blogToc : NonNullable < ReturnType < typeof useBlogToc > > ;
808+ maxWidthClass : string ;
809+ } ) {
810+ const { toc, activeId, scrollToHeading } = blogToc ;
811+ const activeIndex = toc . findIndex ( ( item ) => item . id === activeId ) ;
812+ const activeItem = activeIndex >= 0 ? toc [ activeIndex ] : toc [ 0 ] ;
813+
814+ const goPrev = ( ) => {
815+ const prevIndex = Math . max ( 0 , activeIndex - 1 ) ;
816+ scrollToHeading ( toc [ prevIndex ] . id ) ;
817+ } ;
818+
819+ const goNext = ( ) => {
820+ const nextIndex = Math . min ( toc . length - 1 , activeIndex + 1 ) ;
821+ scrollToHeading ( toc [ nextIndex ] . id ) ;
822+ } ;
823+
824+ return (
825+ < div
826+ className = { `${ maxWidthClass } mx-auto border-x border-neutral-100 border-t border-t-neutral-50 sm:hidden` }
827+ >
828+ < div className = "flex items-center h-11 px-2" >
829+ < button
830+ onClick = { goPrev }
831+ disabled = { activeIndex <= 0 }
832+ className = { cn ( [
833+ "shrink-0 p-1.5 rounded-md transition-colors cursor-pointer" ,
834+ activeIndex <= 0
835+ ? "text-neutral-200"
836+ : "text-neutral-500 hover:text-stone-700 hover:bg-stone-50" ,
837+ ] ) }
838+ >
839+ < ChevronLeft size = { 14 } />
840+ </ button >
841+ < button
842+ onClick = { ( ) => {
843+ if ( activeItem ) scrollToHeading ( activeItem . id ) ;
844+ } }
845+ className = "flex-1 min-w-0 px-2 cursor-pointer"
846+ >
847+ < p className = "text-sm text-stone-700 font-medium truncate text-center" >
848+ { activeItem ?. text }
849+ </ p >
850+ </ button >
851+ < button
852+ onClick = { goNext }
853+ disabled = { activeIndex >= toc . length - 1 }
854+ className = { cn ( [
855+ "shrink-0 p-1.5 rounded-md transition-colors cursor-pointer" ,
856+ activeIndex >= toc . length - 1
857+ ? "text-neutral-200"
858+ : "text-neutral-500 hover:text-stone-700 hover:bg-stone-50" ,
859+ ] ) }
860+ >
861+ < ChevronRight size = { 14 } />
862+ </ button >
863+ </ div >
864+ </ div >
865+ ) ;
866+ }
867+
791868function MobileMenuCTAs ( {
792869 platform,
793870 platformCTA,
0 commit comments