@@ -23,6 +23,7 @@ type NavigationStateType = {
2323
2424const HeaderHeight : CSSProperties [ "height" ] = "3.625rem" ;
2525const BreadCrumbHeight : CSSProperties [ "height" ] = "4.5rem" ;
26+ const MaxContentWidth : CSSProperties [ "maxWidth" ] = "1366px" ;
2627
2728export default function Header ( ) {
2829 const { title, language, siteMapNode, currentSiteMapDepth, shouldShowTitleBanner } = useAppContext ( ) ;
@@ -49,48 +50,56 @@ export default function Header() {
4950 const headerStyle : SxProps < Theme > = shouldShowTitleBanner ? { } : { backgroundColor : "transparent" } ;
5051
5152 return (
52- < Box sx = { { position : "relative" } } onMouseLeave = { resetDepths } >
53+ < Box
54+ sx = { {
55+ position : "relative" ,
56+ "&:has(.nav-dropdown:hover) .header-title-text" : { opacity : 1 } ,
57+ } }
58+ onMouseLeave = { resetDepths }
59+ >
5360 < HeaderContainer sx = { headerStyle } >
54- < NavSideElementContainer >
55- < Link to = "/" onClick = { resetDepths } >
56- < Stack direction = "row" alignItems = "center" spacing = { 0.75 } >
57- < PythonKorea style = { { width : 36 , height : 36 } } />
58- < Typography className = "header-title-text" sx = { { color : "#ededde" , fontWeight : 600 , fontSize : "1rem" , letterSpacing : "0.01em" } } >
59- PyCon Korea 2026
60- </ Typography >
61- </ Stack >
62- </ Link >
63- </ NavSideElementContainer >
61+ < HeaderInner >
62+ < NavSideElementContainer >
63+ < Link to = "/" onClick = { resetDepths } >
64+ < Stack direction = "row" alignItems = "center" spacing = { 0.75 } >
65+ < PythonKorea style = { { width : 36 , height : 36 } } />
66+ < Typography className = "header-title-text" sx = { { color : "#ededde" , fontWeight : 600 , fontSize : "1rem" } } >
67+ PyCon Korea 2026
68+ </ Typography >
69+ </ Stack >
70+ </ Link >
71+ </ NavSideElementContainer >
6472
65- { siteMapNode ? (
66- < Stack direction = "row" alignItems = "center" justifyContent = "center" spacing = { 0.5 } >
67- { Object . values ( siteMapNode . children )
68- . filter ( ( s ) => ! s . hide )
69- . map ( ( r ) => (
70- < Link
71- key = { r . id }
72- onClick = { resetDepths }
73- target = { isString ( r . external_link ) ? "_blank" : undefined }
74- rel = { isString ( r . external_link ) ? "noopener noreferrer" : undefined }
75- to = { r . external_link || r . route_code }
76- >
77- < NavButton onMouseEnter = { ( ) => setDepth1 ( r ) } isActive = { navState . depth1 ?. id === r . id } >
78- { r . name }
79- </ NavButton >
80- </ Link >
81- ) ) }
82- </ Stack >
83- ) : (
84- < CircularProgress size = { 24 } sx = { { color : "#ed5ebd" } } />
85- ) }
73+ { siteMapNode ? (
74+ < Stack direction = "row" alignItems = "center" justifyContent = "center" spacing = { 0.5 } >
75+ { Object . values ( siteMapNode . children )
76+ . filter ( ( s ) => ! s . hide )
77+ . map ( ( r ) => (
78+ < Link
79+ key = { r . id }
80+ onClick = { resetDepths }
81+ target = { isString ( r . external_link ) ? "_blank" : undefined }
82+ rel = { isString ( r . external_link ) ? "noopener noreferrer" : undefined }
83+ to = { r . external_link || r . route_code }
84+ >
85+ < NavButton onMouseEnter = { ( ) => setDepth1 ( r ) } isActive = { navState . depth1 ?. id === r . id } >
86+ { r . name }
87+ </ NavButton >
88+ </ Link >
89+ ) ) }
90+ </ Stack >
91+ ) : (
92+ < CircularProgress size = { 24 } sx = { { color : "#ed5ebd" } } />
93+ ) }
8694
87- < NavSideElementContainer sx = { { justifyContent : "flex-end" } } >
88- < LanguageSelector />
89- </ NavSideElementContainer >
95+ < NavSideElementContainer sx = { { justifyContent : "flex-end" } } >
96+ < LanguageSelector />
97+ </ NavSideElementContainer >
98+ </ HeaderInner >
9099 </ HeaderContainer >
91100
92101 { navState . depth1 && (
93- < NavDropdownOuter >
102+ < NavDropdownOuter className = "nav-dropdown" >
94103 < NavDropdownInner >
95104 < Typography variant = "h2" sx = { { fontSize : "1.5rem" , fontWeight : 700 , color : "#ededde" } } >
96105 { navState . depth1 . name }
@@ -147,22 +156,24 @@ export default function Header() {
147156 { shouldShowTitleBanner && (
148157 < >
149158 < BreadCrumbContainer >
150- < Stack direction = "row" alignItems = "center" spacing = { 0.5 } >
151- { breadCrumbArray
152- . filter ( ( routeInfo ) => isNonNullish ( routeInfo ) )
153- . map ( ( { route_code, name } , index ) => {
154- breadCrumbRoute += `${ route_code } /` ;
155- return (
156- < Fragment key = { index } >
157- { index > 0 && < ArrowForwardIos sx = { { fontSize : "0.75rem" , color : "rgba(237,94,189,0.6)" } } /> }
158- < Link to = { breadCrumbRoute } children = { name } />
159- </ Fragment >
160- ) ;
161- } ) }
162- </ Stack >
163- < Typography variant = "h1" sx = { { fontSize : "1.625rem" , fontWeight : 700 , color : "#ededde" } } >
164- { title }
165- </ Typography >
159+ < BreadCrumbInner >
160+ < Stack direction = "row" alignItems = "center" spacing = { 0.5 } >
161+ { breadCrumbArray
162+ . filter ( ( routeInfo ) => isNonNullish ( routeInfo ) )
163+ . map ( ( { route_code, name } , index ) => {
164+ breadCrumbRoute += `${ route_code } /` ;
165+ return (
166+ < Fragment key = { index } >
167+ { index > 0 && < ArrowForwardIos sx = { { fontSize : "0.75rem" , color : "rgba(237,94,189,0.6)" } } /> }
168+ < Link to = { breadCrumbRoute } children = { name } />
169+ </ Fragment >
170+ ) ;
171+ } ) }
172+ </ Stack >
173+ < Typography variant = "h1" sx = { { fontSize : "1.625rem" , fontWeight : 700 , color : "#ededde" } } >
174+ { title }
175+ </ Typography >
176+ </ BreadCrumbInner >
166177 </ BreadCrumbContainer >
167178 < Box sx = { { height : `calc(${ HeaderHeight } + ${ BreadCrumbHeight } )` } } />
168179 </ >
@@ -180,10 +191,6 @@ const ResponsivePadding = ({ theme }: MUIStyledCommonProps) => ({
180191
181192const HeaderContainer = styled ( "header" ) ( ( { theme } ) => ( {
182193 position : "fixed" ,
183- display : "flex" ,
184- flexDirection : "row" ,
185- justifyContent : "space-between" ,
186- alignItems : "center" ,
187194 width : "100%" ,
188195 height : HeaderHeight ,
189196 backgroundColor : "rgba(18, 9, 30, 0.85)" ,
@@ -195,12 +202,25 @@ const HeaderContainer = styled("header")(({ theme }) => ({
195202 zIndex : theme . zIndex . appBar ,
196203 transition : "background-color 0.3s ease-in-out" ,
197204 "& .header-title-text" : {
198- opacity : 0 ,
205+ // TODO: FIXME: HeaderInner의 좌측 정렬 모드를 중앙 정렬("1fr auto 1fr")로 되돌릴 때 opacity를 다시 0으로 변경할 것 (hover 시에만 노출되는 원래 동작 복귀)
206+ opacity : 1 ,
199207 transition : "opacity 0.2s ease" ,
200208 } ,
201209 "&:hover .header-title-text" : {
202210 opacity : 1 ,
203211 } ,
212+ } ) ) ;
213+
214+ const HeaderInner = styled ( "div" ) ( ( { theme } ) => ( {
215+ display : "grid" ,
216+ // TODO: FIXME: sitemap 항목이 충분히 등록되면 gridTemplateColumns를 "1fr auto 1fr"로 되돌려 중앙 정렬로 복귀하고, columnGap도 제거할 것
217+ gridTemplateColumns : "auto auto 1fr" ,
218+ columnGap : theme . spacing ( 2 ) ,
219+ alignItems : "center" ,
220+ width : "100%" ,
221+ height : "100%" ,
222+ maxWidth : MaxContentWidth ,
223+ marginInline : "auto" ,
204224 ...ResponsivePadding ( { theme } ) ,
205225} ) ) ;
206226
@@ -214,7 +234,10 @@ const NavButton = styled(Button)<{ isActive?: boolean }>(({ isActive }) => ({
214234 "&:hover" : { color : "#ed5ebd" , backgroundColor : "transparent" } ,
215235} ) ) ;
216236
217- const NavSideElementContainer = styled ( Stack ) ( { flexGrow : 1 , flexBasis : 0 } ) ;
237+ const NavSideElementContainer = styled ( Stack ) ( {
238+ flexDirection : "row" ,
239+ alignItems : "center" ,
240+ } ) ;
218241
219242const NavDropdownOuter = styled ( Stack ) ( ( { theme } ) => ( {
220243 width : "100vw" ,
@@ -231,6 +254,8 @@ const NavDropdownOuter = styled(Stack)(({ theme }) => ({
231254
232255const NavDropdownInner = styled ( Stack ) ( ( { theme } ) => ( {
233256 width : "100%" ,
257+ maxWidth : MaxContentWidth ,
258+ marginInline : "auto" ,
234259 minHeight : "10rem" ,
235260 overflowY : "auto" ,
236261 gap : "1rem" ,
@@ -261,7 +286,7 @@ const Depth2to3Divider = styled(Divider)({ borderColor: "rgba(237, 94, 189, 0.3)
261286
262287const Depth3Item = styled ( Depth2Item ) ( { fontSize : "0.75rem" } ) ;
263288
264- const BreadCrumbContainer = styled ( Stack ) ( ( { theme } ) => ( {
289+ const BreadCrumbContainer = styled ( "div" ) ( ( { theme } ) => ( {
265290 position : "fixed" ,
266291 top : HeaderHeight ,
267292 width : "100%" ,
@@ -271,10 +296,17 @@ const BreadCrumbContainer = styled(Stack)(({ theme }) => ({
271296 backdropFilter : "blur(10px)" ,
272297 WebkitBackdropFilter : "blur(10px)" ,
273298 borderBottom : "1px solid rgba(237, 94, 189, 0.15)" ,
299+ zIndex : theme . zIndex . appBar - 1 ,
300+ } ) ) ;
301+
302+ const BreadCrumbInner = styled ( Stack ) ( ( { theme } ) => ( {
303+ width : "100%" ,
304+ height : "100%" ,
305+ maxWidth : MaxContentWidth ,
306+ marginInline : "auto" ,
274307 gap : "0.25rem" ,
275308 justifyContent : "center" ,
276309 alignItems : "flex-start" ,
277- zIndex : theme . zIndex . appBar - 1 ,
278310 ...ResponsivePadding ( { theme } ) ,
279311 "& a" : {
280312 color : "#f5c73d" ,
0 commit comments