Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 14 additions & 11 deletions apps/pyconkr-2026/index.html
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
<!doctype html>
<html lang="en">
<head>
<meta charSet="UTF-8" />
<meta charset="UTF-8" />
<base href="/" />
<link rel="icon" href="/favicon.ico" sizes="32x32">
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="/favicon-180.png">
<link rel="icon" href="/favicon.ico" sizes="32x32" />
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
<link rel="apple-touch-icon" href="/favicon-180.png" />

<meta name="theme-color" content="#fff" />
<meta name="theme-color" media="(prefers-color-scheme: light)" content="#fff" />
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#fff" />
<meta name="theme-color" content="#12091e" />
<meta name="theme-color" media="(prefers-color-scheme: light)" content="#12091e" />
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#12091e" />

<meta name="msapplication-navbutton-color" content="#fff" />
<meta name="msapplication-TileColor" content="#fff" />
<meta name="msapplication-navbutton-color" content="#12091e" />
<meta name="msapplication-TileColor" content="#12091e" />
<meta name="msapplication-TileImage" content="/favicon-192.png" />
<meta name="application-name" content="PyCon KR" />
<meta name="apple-mobile-web-app-title" content="PyCon KR" />
Expand All @@ -22,15 +22,18 @@
<!-- https://developers.google.com/web/fundamentals/web-app-manifest/ -->
<link rel="manifest" href="/site.webmanifest" />

<meta name="viewport" content="width=device-width,
<meta
name="viewport"
content="width=device-width,
height=device-height,
target-densitydpi=device-dpi,
initial-scale=1.0,
minimum-scale=1.0,
maximum-scale=1.0,
user-scalable=0,
user-scalable=no,
shrink-to-fit=no" />
shrink-to-fit=no"
/>
<meta name="author" content="PyCon Korea Organizing Team" />
<meta name="description" content="Teaser site for PyCon Korea 2026" />
<meta name="keywords" content="PyCon, Python, Conference, Korea, 2026" />
Expand Down
22 changes: 11 additions & 11 deletions apps/pyconkr-2026/package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"name": "@apps/pyconkr-2026",
"dependencies": {
"@frontend/common": "workspace:*",
"@frontend/shop": "workspace:*"
},
"devDependencies": {
"vite": "^6.3.5",
"vite-plugin-mdx": "^3.6.1",
"vite-plugin-mkcert": "^1.17.8",
"vite-plugin-svgr": "^4.3.0"
}
"name": "@apps/pyconkr-2026",
"dependencies": {
"@frontend/common": "workspace:*",
"@frontend/shop": "workspace:*"
},
"devDependencies": {
"vite": "^6.3.5",
"vite-plugin-mdx": "^3.6.1",
"vite-plugin-mkcert": "^1.17.8",
"vite-plugin-svgr": "^4.3.0"
}
}
2 changes: 1 addition & 1 deletion apps/pyconkr-2026/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { useBackendClient, useFlattenSiteMapQuery, useSponsorQuery } from "@frontend/common/src/hooks/useAPI";
import * as BackendAPISchemas from "@frontend/common/src/schemas/backendAPI";
import { buildNestedSiteMap } from "@frontend/common/src/utils";
import * as React from "react";
import { Route, Routes, useLocation } from "react-router-dom";
import * as R from "remeda";

import * as BackendAPISchemas from "@frontend/common/src/schemas/backendAPI";
import MainLayout from "./components/layout/index.tsx";
import { PageIdParamRenderer, RouteRenderer } from "./components/pages/dynamic_route.tsx";
import { PresentationDetailPage } from "./components/pages/presentation_detail.tsx";
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
167 changes: 167 additions & 0 deletions apps/pyconkr-2026/src/components/layout/Footer/Mobile/MobileFooter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import styled from "@emotion/styled";
import { useEmail } from "@frontend/common/src/hooks/useEmail";
import { Article, Email, Facebook, GitHub, Instagram, LinkedIn, X, YouTube } from "@mui/icons-material";
import * as React from "react";

import FlickrIcon from "@apps/pyconkr-2026/assets/thirdparty/flickr.svg?react";

import { useAppContext } from "../../../../contexts/app_context";

interface IconItem {
icon: React.FC<{ width?: number; height?: number }>;
alt: string;
href: string;
}

const defaultIcons: IconItem[] = [
{ icon: Facebook, alt: "facebook", href: "https://www.facebook.com/pyconkorea/" },
{ icon: YouTube, alt: "YouTube", href: "https://www.youtube.com/c/PyConKRtube" },
{ icon: X, alt: "X", href: "https://x.com/PyConKR" },
{ icon: GitHub, alt: "github", href: "https://github.com/pythonkr" },
{ icon: Instagram, alt: "Instagram", href: "https://www.instagram.com/pycon_korea/" },
{ icon: LinkedIn, alt: "LinkedIn", href: "https://www.linkedin.com/company/pyconkorea/" },
{ icon: Article, alt: "blog", href: "https://blog.pycon.kr/" },
{ icon: FlickrIcon, alt: "Flickr", href: "https://www.flickr.com/photos/126829363@N08/" },
];

export default function MobileFooter() {
const { sendEmail } = useEmail();
const { language } = useAppContext();

const title = language === "ko" ? "파이콘 한국 2026" : "PyCon Korea 2026";
const committeeTitle =
language === "ko"
? "파이콘 한국 2026은 파이콘 한국 준비위원회가 만들고 있습니다"
: "PyCon Korea 2026 is organized by the PyCon Korea Organizing Committee";
const djangoTitle = language === "ko" ? "파이썬 웹 프레임워크 Django로 만들었습니다" : "Built with the Django web framework for Python";

const links = [
{
text: language === "ko" ? "파이콘 한국 행동 강령(CoC)" : "PyCon Korea Code of Conduct",
href: "https://pythonkr.github.io/pycon-code-of-conduct/ko/coc/a_intent_and_purpose.html",
},
{ text: language === "ko" ? "서비스 이용 약관" : "Terms of Service", href: "/about/terms-of-service" },
{ text: language === "ko" ? "개인 정보 처리 방침" : "Privacy Policy", href: "/about/privacy-policy" },
];

return (
<FooterContainer>
<FooterContent>
<FooterSlogan>
<br />
<FooterBoldText children={title} />
<br />
<FooterNormalText children={committeeTitle} />
<br />
<FooterNormalText children={djangoTitle} />
<br />
</FooterSlogan>
<FooterLinks>
{links.map((link, index) => (
<FooterLinkSlogan key={index}>
<Link href={link.href}>{link.text}</Link>
{index < links.length - 1 && <Separator>|</Separator>}
</FooterLinkSlogan>
))}
</FooterLinks>
<FooterIcons>
<IconLink onClick={sendEmail} aria-label="이메일 보내기">
<Email width={20} height={20} aria-hidden="true" />
</IconLink>
{defaultIcons.map((icon) => (
<IconLink key={icon.alt} href={icon.href} target="_blank" rel="noopener noreferrer" aria-label={`${icon.alt}로 이동`}>
<icon.icon width={20} height={20} aria-hidden="true" />
</IconLink>
))}
</FooterIcons>
</FooterContent>
</FooterContainer>
);
}

const FooterContainer = styled.footer`
background: linear-gradient(
to bottom,
${({ theme }) => theme.palette.background.default} 0%,
${({ theme }) => theme.palette.background.paper} 25%,
${({ theme }) => theme.palette.primary.dark} 75%,
${({ theme }) => theme.palette.primary.main} 100%
);
color: ${({ theme }) => theme.palette.text.primary};
font-size: 0.75rem;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
max-height: 16rem;
padding: 5rem 0 1rem 0;
`;

const FooterContent = styled.div`
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 0.75rem;
`;

const FooterBoldText = styled.text`
font-weight: 600;
`;

const FooterNormalText = styled.text`
font-weight: 400;
`;

const FooterSlogan = styled.div`
text-align: center;
`;

const FooterLinkSlogan = styled.div`
display: flex;
gap: 0.3rem;
`;

const FooterLinks = styled.div`
display: flex;
align-items: center;
gap: 0.3rem;
`;

const FooterIcons = styled.div`
display: flex;
align-items: center;
gap: 9px;
`;

const Link = styled.a`
color: ${({ theme }) => theme.palette.text.primary};
text-decoration: none;
&:hover {
text-decoration: underline;
}
`;

const Separator = styled.span`
color: ${({ theme }) => theme.palette.text.primary};
opacity: 0.5;
margin: 0.05rem 0;
`;

const IconLink = styled.a`
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
color: ${({ theme }) => theme.palette.text.primary};

&:hover {
opacity: 0.8;
}

img {
width: 20px;
height: 20px;
}
`;
17 changes: 14 additions & 3 deletions apps/pyconkr-2026/src/components/layout/Footer/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import styled from "@emotion/styled";
import { useEmail } from "@frontend/common/src/hooks/useEmail";
import { Article, Email, Facebook, GitHub, Instagram, LinkedIn, OpenInNew, X, YouTube } from "@mui/icons-material";
import { Button } from "@mui/material";
import { Button, useMediaQuery, useTheme } from "@mui/material";
import * as React from "react";

import FlickrIcon from "@apps/pyconkr-2026/assets/thirdparty/flickr.svg?react";

import { useAppContext } from "../../../contexts/app_context";
import MobileFooter from "./Mobile/MobileFooter";

interface IconItem {
icon: React.FC<{ width?: number; height?: number }>;
Expand Down Expand Up @@ -49,9 +50,13 @@ const Bar: React.FC = () => <div style={{ display: "inline-block", padding: "0 0

export default function Footer() {
const { sendEmail } = useEmail();
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down("md"));

const { language } = useAppContext();

if (isMobile) return <MobileFooter />;

const corpPasamoStr = language === "ko" ? "사단법인 파이썬사용자모임" : "Python Korea";
const corpAddressStr =
language === "ko" ? "서울특별시 강남구 강남대로84길 24-4" : "24-4, Gangnam-daero 84-gil, Gangnam-gu, Seoul, Republic of Korea";
Expand Down Expand Up @@ -137,8 +142,14 @@ export default function Footer() {
}

const FooterContainer = styled.footer`
background-color: ${({ theme }) => theme.palette.primary.main};
color: ${({ theme }) => theme.palette.common.white};
background: linear-gradient(
to bottom,
${({ theme }) => theme.palette.background.default} 0%,
${({ theme }) => theme.palette.background.paper} 25%,
${({ theme }) => theme.palette.primary.dark} 75%,
${({ theme }) => theme.palette.primary.main} 100%
);
color: ${({ theme }) => theme.palette.text.primary};
font-size: 0.75rem;
display: flex;
flex-direction: column;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { IconButton, styled } from "@mui/material";
import * as React from "react";

interface HamburgerButtonProps {
isOpen: boolean;
onClick: () => void;
}

export const HamburgerButton: React.FC<HamburgerButtonProps> = ({ isOpen, onClick }) => {
return (
<StyledIconButton onClick={onClick}>
<HamburgerIcon isOpen={isOpen}>
<span />
<span />
<span />
</HamburgerIcon>
</StyledIconButton>
);
};

const StyledIconButton = styled(IconButton)({
padding: 0,
width: 26,
height: 18,
color: "#ededde",
});

const HamburgerIcon = styled("div")<{ isOpen: boolean }>(({ isOpen }) => ({
width: 26,
height: 18,
position: "relative",
cursor: "pointer",
display: "flex",
flexDirection: "column",
justifyContent: "space-between",

"& span": {
display: "block",
height: isOpen ? 3 : 2,
width: "100%",
backgroundColor: "#ededde",
borderRadius: 1,
transition: "height 0.3s ease",
},
}));
Loading