Skip to content

Commit ad8077a

Browse files
committed
fix: 헤더가 없는 경우 페이지 상단이 너무 좁아지는 문제 해소
1 parent 0d13698 commit ad8077a

3 files changed

Lines changed: 75 additions & 61 deletions

File tree

apps/pyconkr-2025/src/components/layout/Header/index.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { NestedSiteMapSchema } from "@frontend/common/schemas/backendAPI";
33
import { ArrowForwardIos } from "@mui/icons-material";
44
import { Box, Button, CircularProgress, Divider, Stack, styled, SxProps, Theme, Typography, useMediaQuery, useTheme } from "@mui/material";
55
import { MUIStyledCommonProps } from "@mui/system";
6-
import { CSSProperties, FC, Fragment, useEffect, useState } from "react";
6+
import { CSSProperties, Fragment, useEffect, useState } from "react";
77
import { Link } from "react-router-dom";
88
import { isEmpty, isNonNullish, isString } from "remeda";
99

@@ -26,7 +26,7 @@ type NavigationStateType = {
2626
const HeaderHeight: CSSProperties["height"] = "3.625rem";
2727
const BreadCrumbHeight: CSSProperties["height"] = "4.5rem";
2828

29-
const Header: FC = () => {
29+
export default function Header() {
3030
const { title, language, siteMapNode, currentSiteMapDepth, shouldShowTitleBanner } = useAppContext();
3131
const theme = useTheme();
3232
const isMobile = useMediaQuery(theme.breakpoints.down("md"));
@@ -105,7 +105,6 @@ const Header: FC = () => {
105105
key={r.id}
106106
onClick={resetDepths}
107107
onMouseEnter={() => setDepth2(r)}
108-
// 하위 depth가 있는 경우, 하위 depth를 선택할 수 있도록 유지하기 위해 depth2도 유지합니다.
109108
onMouseLeave={() => isEmpty(navState.depth2?.children ?? {}) && setDepth2(undefined)}
110109
target={isString(r.external_link) ? "_blank" : undefined}
111110
rel={isString(r.external_link) ? "noopener noreferrer" : undefined}
@@ -179,7 +178,7 @@ const Header: FC = () => {
179178
)}
180179
</Box>
181180
);
182-
};
181+
}
183182

184183
const ResponsivePaddingDefinition = ({ theme }: MUIStyledCommonProps) => ({
185184
paddingRight: theme!.spacing(16),
@@ -309,5 +308,3 @@ const BreadCrumbContainer = styled(Stack)(({ theme }) => ({
309308
fontSize: "0.75rem",
310309
},
311310
}));
312-
313-
export default Header;

apps/pyconkr-2026/src/components/layout/Header/index.tsx

Lines changed: 71 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import LanguageSelector from "@apps/pyconkr-2026/components/layout/LanguageSelec
1111
import { UserMenuButton } from "@apps/pyconkr-2026/components/layout/UserMenuButton";
1212
import { useAppContext } from "@apps/pyconkr-2026/contexts/app_context";
1313

14-
import { MobileHeader } from "./Mobile/MobileHeader";
1514
import { PyConLogo } from "../../pycon_logo";
15+
import { MobileHeader } from "./Mobile/MobileHeader";
1616

1717
type MenuType = NestedSiteMapSchema;
1818
type MenuOrUndefinedType = MenuType | undefined;
@@ -43,7 +43,9 @@ export default function Header() {
4343

4444
useEffect(resetDepths, [language]);
4545

46-
if (isMobile) return <MobileHeader />;
46+
if (isMobile) {
47+
return <MobileHeader />;
48+
}
4749

4850
let breadCrumbRoute = "";
4951
let breadCrumbArray = currentSiteMapDepth.slice(1, -1);
@@ -52,13 +54,7 @@ export default function Header() {
5254
const headerStyle: SxProps<Theme> = shouldShowTitleBanner ? {} : { backgroundColor: "transparent" };
5355

5456
return (
55-
<Box
56-
sx={{
57-
position: "relative",
58-
"&:has(.nav-dropdown:hover) .header-title-text": { opacity: 1 },
59-
}}
60-
onMouseLeave={resetDepths}
61-
>
57+
<Box sx={{ position: "relative" }} onMouseLeave={resetDepths}>
6258
<HeaderContainer sx={headerStyle}>
6359
<HeaderInner>
6460
<NavSideElementContainer>
@@ -71,7 +67,6 @@ export default function Header() {
7167
</Stack>
7268
</Link>
7369
</NavSideElementContainer>
74-
7570
{siteMapNode ? (
7671
<Stack direction="row" alignItems="center" justifyContent="center" spacing={0.5}>
7772
{Object.values(siteMapNode.children)
@@ -107,20 +102,20 @@ export default function Header() {
107102
</HeaderContainer>
108103

109104
{navState.depth1 && (
110-
<NavDropdownOuter className="nav-dropdown">
111-
<NavDropdownInner>
112-
<Typography variant="h2" sx={{ fontSize: "1.5rem", fontWeight: 700, color: "#ededde" }}>
105+
<NavOuterContainer>
106+
<NavInnerContainer>
107+
<Typography variant="h2" sx={{ fontSize: "1.5rem", fontWeight: 700 }}>
113108
{navState.depth1.name}
114109
</Typography>
115-
<HighlightDivider flexItem />
110+
<Depth1to2Divider flexItem />
116111
<Stack direction="row" spacing={4}>
117112
<Stack spacing={1.25}>
118113
{Object.values(navState.depth1.children)
119114
.filter((s) => !s.hide)
120115
.map((r) => (
121116
<Depth2Item
122-
key={r.id}
123117
className={r.id === navState.depth2?.id ? "active" : ""}
118+
key={r.id}
124119
onClick={resetDepths}
125120
onMouseEnter={() => setDepth2(r)}
126121
onMouseLeave={() => isEmpty(navState.depth2?.children ?? {}) && setDepth2(undefined)}
@@ -136,14 +131,15 @@ export default function Header() {
136131

137132
{navState.depth2 && !isEmpty(navState.depth2.children) && (
138133
<>
139-
<Depth2to3Divider orientation="vertical" flexItem />
134+
{!isEmpty(navState.depth2.children) && <Depth2to3Divider orientation="vertical" flexItem />}
135+
140136
<Stack spacing={1.5}>
141137
{Object.values(navState.depth2.children)
142138
.filter((s) => !s.hide)
143139
.map((r) => (
144140
<Depth3Item
145-
key={r.id}
146141
className={r.id === navState.depth3?.id ? "active" : ""}
142+
key={r.id}
147143
onClick={resetDepths}
148144
onMouseEnter={() => setDepth3(r)}
149145
onMouseLeave={() => setDepth3(undefined)}
@@ -159,11 +155,10 @@ export default function Header() {
159155
</>
160156
)}
161157
</Stack>
162-
</NavDropdownInner>
163-
</NavDropdownOuter>
158+
</NavInnerContainer>
159+
</NavOuterContainer>
164160
)}
165-
166-
{shouldShowTitleBanner && (
161+
{shouldShowTitleBanner ? (
167162
<>
168163
<BreadCrumbContainer>
169164
<BreadCrumbInner>
@@ -185,18 +180,28 @@ export default function Header() {
185180
</Typography>
186181
</BreadCrumbInner>
187182
</BreadCrumbContainer>
183+
{/* Spacer for fixed header */}
188184
<Box sx={{ height: `calc(${HeaderHeight} + ${BreadCrumbHeight})` }} />
189185
</>
186+
) : (
187+
<Box sx={{ height: HeaderHeight }} />
190188
)}
191189
</Box>
192190
);
193191
}
194192

195-
const ResponsivePadding = ({ theme }: MUIStyledCommonProps) => ({
193+
const ResponsivePaddingDefinition = ({ theme }: MUIStyledCommonProps) => ({
196194
paddingRight: theme!.spacing(16),
197195
paddingLeft: theme!.spacing(16),
198-
[theme!.breakpoints.down("lg")]: { paddingRight: theme!.spacing(4), paddingLeft: theme!.spacing(4) },
199-
[theme!.breakpoints.down("sm")]: { paddingRight: theme!.spacing(2), paddingLeft: theme!.spacing(2) },
196+
197+
[theme!.breakpoints.down("lg")]: {
198+
paddingRight: theme!.spacing(4),
199+
paddingLeft: theme!.spacing(4),
200+
},
201+
[theme!.breakpoints.down("sm")]: {
202+
paddingRight: theme!.spacing(2),
203+
paddingLeft: theme!.spacing(2),
204+
},
200205
});
201206

202207
const HeaderContainer = styled("header")(({ theme }) => ({
@@ -207,12 +212,13 @@ const HeaderContainer = styled("header")(({ theme }) => ({
207212
backdropFilter: "blur(12px)",
208213
WebkitBackdropFilter: "blur(12px)",
209214
borderBottom: "1px solid rgba(237, 94, 189, 0.2)",
210-
color: "#ededde",
215+
color: theme.palette.text.primary,
216+
211217
fontWeight: 500,
218+
212219
zIndex: theme.zIndex.appBar,
213220
transition: "background-color 0.3s ease-in-out",
214221
"& .header-title-text": {
215-
// TODO: FIXME: HeaderInner의 좌측 정렬 모드를 중앙 정렬("1fr auto 1fr")로 되돌릴 때 opacity를 다시 0으로 변경할 것 (hover 시에만 노출되는 원래 동작 복귀)
216222
opacity: 1,
217223
transition: "opacity 0.2s ease",
218224
},
@@ -223,84 +229,90 @@ const HeaderContainer = styled("header")(({ theme }) => ({
223229

224230
const HeaderInner = styled("div")(({ theme }) => ({
225231
display: "grid",
226-
// TODO: FIXME: sitemap 항목이 충분히 등록되면 gridTemplateColumns를 "1fr auto 1fr"로 되돌려 중앙 정렬로 복귀하고, columnGap도 제거할 것
227-
gridTemplateColumns: "auto auto 1fr",
228-
columnGap: theme.spacing(2),
232+
gridTemplateColumns: "1fr auto 1fr",
229233
alignItems: "center",
230234
width: "100%",
231235
height: "100%",
232236
maxWidth: MaxContentWidth,
233237
marginInline: "auto",
234-
...ResponsivePadding({ theme }),
238+
...ResponsivePaddingDefinition({ theme }),
235239
}));
236240

237-
const NavButton = styled(Button)<{ isActive?: boolean }>(({ isActive }) => ({
238-
color: isActive ? "#ed5ebd" : "#ededde",
241+
const NavButton = styled(Button)<{ isActive?: boolean }>(({ theme, isActive }) => ({
242+
color: isActive ? theme.palette.primary.main : theme.palette.text.primary,
239243
minWidth: 0,
240244
textTransform: "none",
241245
fontSize: "0.9rem",
242246
fontWeight: isActive ? 700 : 400,
243247
transition: "color 0.2s ease",
244-
"&:hover": { color: "#ed5ebd", backgroundColor: "transparent" },
248+
"&:hover": { color: theme.palette.primary.main, backgroundColor: "transparent" },
245249
}));
246250

247251
const NavSideElementContainer = styled(Stack)({
248252
flexDirection: "row",
249253
alignItems: "center",
250254
});
251255

252-
const NavDropdownOuter = styled(Stack)(({ theme }) => ({
256+
const NavOuterContainer = styled(Stack)(({ theme }) => ({
253257
width: "100vw",
258+
254259
position: "fixed",
255260
left: 0,
256261
top: HeaderHeight,
262+
257263
zIndex: theme.zIndex.appBar + 1,
264+
258265
backgroundColor: "rgba(18, 9, 30, 0.95)",
259266
boxShadow: "0 5px 20px rgba(0, 0, 0, 0.4)",
260267
backdropFilter: "blur(12px)",
268+
261269
WebkitBackdropFilter: "blur(12px)",
262270
borderBottom: "1px solid rgba(237, 94, 189, 0.2)",
263271
}));
264272

265-
const NavDropdownInner = styled(Stack)(({ theme }) => ({
273+
const NavInnerContainer = styled(Stack)(({ theme }) => ({
266274
width: "100%",
267275
maxWidth: MaxContentWidth,
268276
marginInline: "auto",
269277
minHeight: "10rem",
270278
overflowY: "auto",
271279
gap: "1rem",
280+
272281
paddingTop: "1.5rem",
273282
paddingBottom: "2rem",
274-
color: "#ededde",
275-
...ResponsivePadding({ theme }),
283+
284+
...ResponsivePaddingDefinition({ theme }),
276285
}));
277286

278-
const HighlightDivider = styled(Divider)({
287+
const Depth1to2Divider = styled(Divider)(({ theme }) => ({
279288
width: "3rem",
280-
borderBottom: "4px solid #f5c73d",
281-
borderColor: "#f5c73d",
282-
});
289+
borderBottom: `4px solid ${theme.palette.highlight.main}`,
290+
}));
283291

284-
const Depth2Item = styled(Link)({
285-
display: "inline-flex",
286-
alignItems: "center",
287-
gap: "0.25rem",
288-
color: "#ededde",
292+
const Depth2Item = styled(Link)(({ theme }) => ({
293+
color: theme.palette.text.primary,
289294
fontWeight: 300,
290295
textDecoration: "none",
291296
width: "fit-content",
292297
borderBottom: "2px solid transparent",
293-
transition: "color 0.15s ease",
294-
"&.active": { fontWeight: 700, borderBottom: "2px solid #ed5ebd", color: "#ed5ebd" },
295-
"&:hover": { color: "#ed5ebd" },
296-
});
298+
299+
"&.active": {
300+
fontWeight: 700,
301+
borderBottom: `2px solid ${theme.palette.primary.main}`,
302+
color: theme.palette.primary.main,
303+
},
304+
"&:hover": {
305+
color: theme.palette.primary.main,
306+
},
307+
}));
297308

298309
const Depth2to3Divider = styled(Divider)({ borderColor: "rgba(237, 94, 189, 0.3)" });
299310

300311
const Depth3Item = styled(Depth2Item)({ fontSize: "0.75rem" });
301312

302-
const BreadCrumbContainer = styled("div")(({ theme }) => ({
313+
const BreadCrumbContainer = styled(Stack)(({ theme }) => ({
303314
position: "fixed",
315+
304316
top: HeaderHeight,
305317
width: "100%",
306318
height: BreadCrumbHeight,
@@ -320,12 +332,17 @@ const BreadCrumbInner = styled(Stack)(({ theme }) => ({
320332
gap: "0.25rem",
321333
justifyContent: "center",
322334
alignItems: "flex-start",
323-
...ResponsivePadding({ theme }),
335+
336+
...ResponsivePaddingDefinition({ theme }),
337+
324338
"& a": {
325-
color: "#f5c73d",
339+
color: theme.palette.highlight.main,
326340
fontWeight: 300,
327341
fontSize: "0.75rem",
328342
textDecoration: "none",
329-
"&:hover": { textDecoration: "underline" },
343+
344+
"&:hover": {
345+
textDecoration: "underline",
346+
},
330347
},
331348
}));

apps/pyconkr-2026/src/components/pages/presentation_detail.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ export const PresentationDetailPage: FC = ErrorBoundary.with(
199199
setAppContext((prev) => ({
200200
...prev,
201201
title: language === "ko" ? "발표 상세" : "Presentation Detail",
202-
shouldShowTitleBanner: true,
202+
shouldShowTitleBanner: false,
203203
shouldShowSponsorBanner: true,
204204
}));
205205
}, [language, presentation, setAppContext]);

0 commit comments

Comments
 (0)