diff --git a/frontend/components/common/Avatar.tsx b/frontend/components/common/Avatar.tsx index 3cedd6182..3dd9a81b5 100644 --- a/frontend/components/common/Avatar.tsx +++ b/frontend/components/common/Avatar.tsx @@ -12,10 +12,10 @@ const S_Container = styled("div", { position: "relative", userSelect: "none", overflow: "hidden", - width: "38px", - minWidth: "38px", - height: "38px", - borderRadius: "16px", + width: "var(--item-size)", + minWidth: "var(--item-size)", + height: "var(--item-size)", + borderRadius: "$rounded", }); const S_Avatar = styled("img", { diff --git a/frontend/components/layout/HamburgerButton.tsx b/frontend/components/layout/HamburgerButton.tsx new file mode 100644 index 000000000..f6a811606 --- /dev/null +++ b/frontend/components/layout/HamburgerButton.tsx @@ -0,0 +1,121 @@ +import { styled } from "stitches.config"; + +export function HamburgerButton({ + isExpanded, + onClick, +}: { + onClick: () => void; + isExpanded: boolean; +}) { + return ( + + + + + + + + ); +} + +const S_TransformingBurger = styled("button", { + display: "flex", + width: "var(--item-size)", + height: "var(--item-size)", + flexDirection: "column", + alignItems: "center", + justifyContent: "center", + padding: "$1", + border: "3px solid", + borderColor: "$bgLighter", + backgroundColor: "transparent", + borderRadius: "$rounded", + color: "inherit", + cursor: "pointer", + gap: "2px", + + "@sm": { + display: "none", + }, +}); + +const S_TopLine = styled("rect", { + transform: "none", + transformOrigin: "16px 10px", + transitionDuration: "150ms", + transitionProperty: "transform", + transitionTimingFunction: "cubic-bezier(0.4, 0, 0.2, 1)", + + variants: { + type: { + expanded: { + transform: "translateY(7px) rotate(45deg)", + }, + }, + }, +}); + +const S_MiddleLine = styled("rect", { + opacity: 1, + transitionDuration: "150ms", + transitionProperty: "opacity", + transitionTimingFunction: "cubic-bezier(0.4, 0, 0.2, 1)", + + variants: { + type: { + expanded: { + opacity: 0, + }, + }, + }, +}); + +const S_BottomLine = styled("rect", { + transform: "none", + transformOrigin: "16px 22px", + transitionDuration: "150ms", + transitionProperty: "transform", + transitionTimingFunction: "cubic-bezier(0.4, 0, 0.2, 1)", + + variants: { + type: { + expanded: { + transform: "translateY(-5px) rotate(-45deg)", + }, + }, + }, +}); diff --git a/frontend/components/layout.tsx b/frontend/components/layout/Layout.tsx similarity index 66% rename from frontend/components/layout.tsx rename to frontend/components/layout/Layout.tsx index bbd1019e6..e08ba2276 100644 --- a/frontend/components/layout.tsx +++ b/frontend/components/layout/Layout.tsx @@ -1,41 +1,36 @@ -import { ReactNode } from "react"; +import { ReactNode, useState } from "react"; import NextImage from "next/image"; -import logo from "../assets/img/logo.png"; -import { Avatar } from "./common/Avatar"; +import NextLink from "next/link"; +import logo from "assets/img/logo.png"; +import { Avatar } from "components/common/Avatar"; import { styled } from "stitches.config"; -import { SearchInput } from "./common/SearchInput"; - -const navItems = [ - { - title: "builds", - items: ["browse", "gear", "analyzer"], - }, - { - title: "play", - items: ["calendar", "battle", "Rankings"], - }, - { - title: "tools", - items: ["planner", "rotations", "top 500"], - }, - { - title: "misc", - items: ["badges", "links"], - }, -]; +import { SearchInput } from "components/common/SearchInput"; +import { HamburgerButton } from "./HamburgerButton"; +import { navItems } from "utils/constants"; +import { MobileNav } from "./MobileNav"; export function Layout({ children }: { children: ReactNode }) { + const [menuExpanded, setMenuExpanded] = useState(false); return ( <> - - - - + + + + + + + + + setMenuExpanded((expanded) => !expanded)} + /> + {navItems.map((navItem) => ( @@ -59,10 +54,19 @@ export function Layout({ children }: { children: ReactNode }) { } const S_Header = styled("header", { + position: "relative", display: "grid", - gridTemplateColumns: "repeat(3, 1fr)", + gridTemplateColumns: "repeat(2, 1fr)", alignItems: "center", padding: "$4", + "--item-size": "3rem", + zIndex: 10, + backgroundColor: "$bg", + + "@sm": { + gridTemplateColumns: "repeat(3, 1fr)", + "--item-size": "38px", + }, }); const S_LogoContainer = styled("div", { @@ -73,6 +77,18 @@ const S_LogoContainer = styled("div", { padding: "$1", borderRadius: "$rounded", justifySelf: "flex-start", + cursor: "pointer", + width: "var(--item-size)", + minWidth: "var(--item-size)", + height: "var(--item-size)", +}); + +const S_SearchContainer = styled("div", { + display: "none", + + "@sm": { + display: "block", + }, }); const S_RightContainer = styled("div", { @@ -82,10 +98,14 @@ const S_RightContainer = styled("div", { }); const S_Nav = styled("nav", { - display: "flex", + display: "none", justifyContent: "center", backgroundColor: "$bgLighter", backgroundImage: `url(/svg/background-pattern.svg)`, + + "@sm": { + display: "flex", + }, }); const S_NavItems = styled("div", { diff --git a/frontend/components/layout/MobileNav.tsx b/frontend/components/layout/MobileNav.tsx new file mode 100644 index 000000000..e81ef50b5 --- /dev/null +++ b/frontend/components/layout/MobileNav.tsx @@ -0,0 +1,113 @@ +import { SearchInput } from "components/common/SearchInput"; +import { styled } from "stitches.config"; +import { navItems } from "utils/constants"; +import NextLink from "next/link"; +import { Fragment } from "react"; + +export function MobileNav({ isExpanded }: { isExpanded: boolean }) { + return ( + + + + + + {navItems.map((navItem) => { + return ( + + {navItem.title} + {navItem.items.map((item) => { + return ( + + + +
{item}
+
+
+ ); + })} +
+ ); + })} +
+
+ ); +} + +const S_Container = styled("nav", { + position: "absolute", + bottom: "100vh", + width: "100%", + height: "100%", + transition: "bottom 0.5s", + transitionTimingFunction: "cubic-bezier(0.4, 0, 0.2, 1)", + backgroundImage: `url(/svg/background-pattern.svg)`, + backgroundColor: "$bgLighter", + zIndex: 5, + overflowY: "auto", + paddingBottom: "$6", + + "@sm": { + display: "none", + }, + + variants: { + type: { + expanded: { + bottom: "0", + }, + }, + }, +}); + +const S_TopAction = styled("div", { + display: "grid", + gap: "$4", + placeItems: "center", + backgroundColor: "$bg", + paddingTop: "$24", + paddingBottom: "$8", +}); + +const S_LinksContainer = styled("div", { + display: "grid", + justifyContent: "center", + gridAutoColumns: "1fr", + gridAutoRows: "4rem", + paddingX: "$24", +}); + +const S_NavLinkImage = styled("img", { + width: "2.5rem", + marginRight: "$3", +}); + +const S_GroupTitle = styled("div", { + textTransform: "uppercase", + alignSelf: "flex-end", + justifySelf: "center", + paddingX: "$3", + paddingY: "$2", + marginBottom: "$1", + backgroundColor: "$bg", + fontWeight: "$bold", +}); + +const S_NavLink = styled("div", { + display: "flex", + alignItems: "center", + textTransform: "capitalize", + borderTop: "3px solid $bgLighter", + fontWeight: "$bold", + backgroundColor: "$bg", + paddingX: "$2", + fontSize: "$sm", + + "&:last-child": { + borderBottom: "3px solid $bgLighter", + }, +}); diff --git a/frontend/pages/_app.tsx b/frontend/pages/_app.tsx index 4d349995e..c26891824 100644 --- a/frontend/pages/_app.tsx +++ b/frontend/pages/_app.tsx @@ -4,7 +4,7 @@ import "./_app.css"; import { AppProps } from "next/app"; import Head from "next/head"; import { SWRConfig } from "swr"; -import { Layout } from "../components/layout"; +import { Layout } from "components/layout/Layout"; import { globalCss } from "stitches.config"; const globalStyles = globalCss({ diff --git a/frontend/stitches.config.ts b/frontend/stitches.config.ts index 68dd54fca..59bee7963 100644 --- a/frontend/stitches.config.ts +++ b/frontend/stitches.config.ts @@ -41,6 +41,7 @@ export const { "0.5": "0.125rem", "1": "0.25rem", "2": "0.5rem", + // TODO: fix vars with . "invalid property" "2.5": "0.625rem", "3": "0.75rem", "3.5": "0.875rem", diff --git a/frontend/utils/constants.ts b/frontend/utils/constants.ts new file mode 100644 index 000000000..f4bec3f1d --- /dev/null +++ b/frontend/utils/constants.ts @@ -0,0 +1,18 @@ +export const navItems = [ + { + title: "builds", + items: ["browse", "gear", "analyzer"], + }, + { + title: "play", + items: ["calendar", "battle", "Rankings"], + }, + { + title: "tools", + items: ["planner", "rotations", "top 500"], + }, + { + title: "misc", + items: ["badges", "links"], + }, +];