1- 'use client'
2-
3- import { useEffect , useRef , useState } from 'react'
41import { JsonLd } from '@/components/JsonLd'
52import { Link } from '@/i18n/navigation'
63import { SITE_CONFIG } from '@/lib/metadata/config'
@@ -14,32 +11,9 @@ export interface BreadcrumbItem {
1411 * Renders a breadcrumb navigation bar and injects a Schema.org BreadcrumbList via JsonLd.
1512 * - Visual trail is rendered from provided items; the last item is shown as plain text.
1613 * - Structured data includes "Home" at the first position for better SEO.
17- * - Sticky behavior is enabled when scrolling.
14+ * - Sticky behavior is enabled when scrolling using CSS position: sticky .
1815 */
1916export function Breadcrumb ( { items } : { items : BreadcrumbItem [ ] } ) {
20- const breadcrumbRef = useRef < HTMLDivElement > ( null )
21- const [ isBreadcrumbFixed , setIsBreadcrumbFixed ] = useState ( false )
22- const [ headerHeight , setHeaderHeight ] = useState ( 0 )
23-
24- useEffect ( ( ) => {
25- const handleScroll = ( ) => {
26- if ( ! breadcrumbRef . current ) return
27- const header = document . querySelector ( 'header' )
28- const currentHeaderHeight = header ?. offsetHeight || 0
29- setHeaderHeight ( currentHeaderHeight )
30- const breadcrumbTop = breadcrumbRef . current . offsetTop
31- setIsBreadcrumbFixed ( window . scrollY > breadcrumbTop - currentHeaderHeight )
32- }
33-
34- window . addEventListener ( 'scroll' , handleScroll )
35- window . addEventListener ( 'resize' , handleScroll )
36- handleScroll ( )
37-
38- return ( ) => {
39- window . removeEventListener ( 'scroll' , handleScroll )
40- window . removeEventListener ( 'resize' , handleScroll )
41- }
42- } , [ ] )
4317 // Normalize href to ensure it starts with '/' (unless it's already an absolute URL)
4418 const normalizeHref = ( href : string ) : string => {
4519 // If it's already an absolute URL or starts with '/', return as is
@@ -74,51 +48,40 @@ export function Breadcrumb({ items }: { items: BreadcrumbItem[] }) {
7448 itemListElement : schemaItems ,
7549 } as const
7650
77- const BreadcrumbContent = ( ) => (
78- < section
79- className = "py-[var(--spacing-sm)] bg-[var(--color-hover)] border-b border-[var(--color-border)]"
80- data-breadcrumb
81- >
82- < div className = "max-w-8xl mx-auto px-[var(--spacing-md)]" >
83- < nav className = "flex items-center gap-[var(--spacing-xs)] text-sm pl-[var(--spacing-xs)]" >
84- { items . map ( ( item , index ) => {
85- const isLast = index === items . length - 1
86- const normalizedHref = normalizeHref ( item . href )
87- return (
88- < span
89- key = { `${ item . href } -${ index } ` }
90- className = "inline-flex items-center gap-[var(--spacing-xs)]"
91- >
92- { isLast ? (
93- < span className = "text-[var(--color-text)] font-medium" > { item . name } </ span >
94- ) : (
95- < Link
96- href = { normalizedHref }
97- className = "text-[var(--color-text-secondary)] hover:text-[var(--color-text)] transition-colors"
98- >
99- { item . name }
100- </ Link >
101- ) }
102- { ! isLast && < span className = "text-[var(--color-text-muted)]" > /</ span > }
103- </ span >
104- )
105- } ) }
106- </ nav >
107- </ div >
108- </ section >
109- )
110-
11151 return (
11252 < >
11353 < JsonLd data = { breadcrumbListSchema } />
114- { isBreadcrumbFixed && (
115- < div className = "fixed left-0 right-0 z-40 shadow-sm" style = { { top : `${ headerHeight } px` } } >
116- < BreadcrumbContent />
54+ < section
55+ className = "sticky top-[4rem] z-40 py-[var(--spacing-sm)] bg-[var(--color-hover)] border-b border-[var(--color-border)] shadow-sm"
56+ data-breadcrumb
57+ >
58+ < div className = "max-w-8xl mx-auto px-[var(--spacing-md)]" >
59+ < nav className = "flex items-center gap-[var(--spacing-xs)] text-sm pl-[var(--spacing-xs)]" >
60+ { items . map ( ( item , index ) => {
61+ const isLast = index === items . length - 1
62+ const normalizedHref = normalizeHref ( item . href )
63+ return (
64+ < span
65+ key = { `${ item . href } -${ index } ` }
66+ className = "inline-flex items-center gap-[var(--spacing-xs)]"
67+ >
68+ { isLast ? (
69+ < span className = "text-[var(--color-text)] font-medium" > { item . name } </ span >
70+ ) : (
71+ < Link
72+ href = { normalizedHref }
73+ className = "text-[var(--color-text-secondary)] hover:text-[var(--color-text)] transition-colors"
74+ >
75+ { item . name }
76+ </ Link >
77+ ) }
78+ { ! isLast && < span className = "text-[var(--color-text-muted)]" > /</ span > }
79+ </ span >
80+ )
81+ } ) }
82+ </ nav >
11783 </ div >
118- ) }
119- < div ref = { breadcrumbRef } className = { isBreadcrumbFixed ? 'invisible' : '' } >
120- < BreadcrumbContent />
121- </ div >
84+ </ section >
12285 </ >
12386 )
12487}
0 commit comments