diff --git a/docs/components/AtlantisThemeContext/AtlantisThemeContext.stories.mdx b/docs/components/AtlantisThemeContext/AtlantisThemeContext.stories.mdx
index 4b7f3f7f8a..8d5026610a 100644
--- a/docs/components/AtlantisThemeContext/AtlantisThemeContext.stories.mdx
+++ b/docs/components/AtlantisThemeContext/AtlantisThemeContext.stories.mdx
@@ -13,8 +13,8 @@ tokens.
## Design & usage guidelines
-Both the web and mobile components have the exact same API, except for one
-minor difference in how you update the theme.
+Both the web and mobile components have the exact same API, except for one minor
+difference in how you update the theme.
Each platform provides a `useAtlantisTheme` hook that you may use to access the
`theme` and `tokens` in your components.
@@ -80,7 +80,10 @@ function ThemedComponent() {
### Usage for mobile
```tsx
-import { AtlantisThemeContextProvider, useAtlantisTheme } from "@jobber/components/AtlantisThemeContext";
+import {
+ AtlantisThemeContextProvider,
+ useAtlantisTheme,
+} from "@jobber/components/AtlantisThemeContext";
function App() {
return (
diff --git a/packages/site/src/layout/AnimatedPresenceDisclosure.tsx b/packages/site/src/layout/AnimatedPresenceDisclosure.tsx
index 14b156bc32..1640567b53 100644
--- a/packages/site/src/layout/AnimatedPresenceDisclosure.tsx
+++ b/packages/site/src/layout/AnimatedPresenceDisclosure.tsx
@@ -2,6 +2,7 @@ import { AnimatedPresence, Button, Typography } from "@jobber/components";
import React, { useEffect, useMemo, useState } from "react";
import { Link, useLocation } from "react-router-dom";
import styles from "./NavMenu.module.css";
+import { useAtlantisSite } from "../providers/AtlantisSiteProvider";
interface AnimatedPresenceDisclosureProps {
readonly children: React.ReactNode;
@@ -23,6 +24,8 @@ function AnimatedPresenceDisclosure({
[children],
);
+ const { toggleMobileMenu } = useAtlantisSite();
+
// Determine if any child is selected based on the current URL
const hasSelectedChild = childrenArray.some(
child => React.isValidElement(child) && pathname === child.props.to,
@@ -63,7 +66,7 @@ function AnimatedPresenceDisclosure({
isTitleSelected ? styles.selected : ""
} stickySectionHeader`}
>
-
+
{title}
diff --git a/packages/site/src/layout/LeftDrawer.module.css b/packages/site/src/layout/LeftDrawer.module.css
new file mode 100644
index 0000000000..4b1e9c6a77
--- /dev/null
+++ b/packages/site/src/layout/LeftDrawer.module.css
@@ -0,0 +1,49 @@
+.drawer {
+ position: fixed;
+ top: 0;
+ left: 0;
+ height: 100%;
+ width: 100%;
+ background: var(--color-surface--background);
+ z-index: var(--elevation-modal);
+ display: flex;
+ flex-direction: column;
+ animation: slideIn 200ms ease-out;
+}
+
+@media screen and (min-width: 768px) {
+ .drawer {
+ display: none;
+ }
+}
+
+.drawerClosing {
+ animation: slideOut 200ms ease-out;
+}
+
+.header {
+ padding: var(--space-base);
+}
+
+.content {
+ flex: 1;
+ overflow-y: hidden;
+}
+
+@keyframes slideIn {
+ from {
+ transform: translateX(-100%);
+ }
+ to {
+ transform: translateX(0);
+ }
+}
+
+@keyframes slideOut {
+ from {
+ transform: translateX(0);
+ }
+ to {
+ transform: translateX(-100%);
+ }
+}
diff --git a/packages/site/src/layout/LeftDrawer.tsx b/packages/site/src/layout/LeftDrawer.tsx
new file mode 100644
index 0000000000..a1aab3a472
--- /dev/null
+++ b/packages/site/src/layout/LeftDrawer.tsx
@@ -0,0 +1,37 @@
+import { Box, Button } from "@jobber/components";
+import { PropsWithChildren, useState } from "react";
+import styles from "./LeftDrawer.module.css";
+
+interface LeftDrawerProps extends PropsWithChildren {
+ readonly onClose: () => void;
+ readonly header?: React.ReactNode;
+}
+
+export function LeftDrawer({ children, onClose, header }: LeftDrawerProps) {
+ const [isClosing, setIsClosing] = useState(false);
+
+ const handleClose = () => {
+ setIsClosing(true);
+ setTimeout(() => {
+ onClose();
+ }, 200);
+ };
+
+ return (
+
+
+
+ {header}
+
+
{children}
+
+ );
+}
diff --git a/packages/site/src/layout/NavMenu.module.css b/packages/site/src/layout/NavMenu.module.css
index faeef746d3..6b41118278 100644
--- a/packages/site/src/layout/NavMenu.module.css
+++ b/packages/site/src/layout/NavMenu.module.css
@@ -5,10 +5,14 @@
--navItemHeight: 40px;
display: flex;
flex-direction: column;
- padding: 35px 0 0 0;
- height: 100dvh;
+ height: 100%;
min-width: var(--sideBarWidth);
box-sizing: border-box;
+
+ @media screen and (min-width: 768px) {
+ padding: 35px 0 0 0;
+ height: 100dvh;
+ }
}
.navMenuContainer>* {
@@ -130,4 +134,20 @@ a.navFooterLink {
margin-top: auto;
font-size: var(--typography--fontSize-small);
color: var(--color-text--secondary);
+}
+
+.desktopNavContainer {
+ display: none;
+
+ @media screen and (min-width: 768px) {
+ display: block;
+ }
+}
+
+.navMenuHeaderLogo {
+ display: none;
+
+ @media screen and (min-width: 768px) {
+ display: block;
+ }
}
\ No newline at end of file
diff --git a/packages/site/src/layout/NavMenu.tsx b/packages/site/src/layout/NavMenu.tsx
index 45581c95ac..e6c90b1d70 100644
--- a/packages/site/src/layout/NavMenu.tsx
+++ b/packages/site/src/layout/NavMenu.tsx
@@ -1,14 +1,16 @@
import { Box, Button, Typography } from "@jobber/components";
import { Link, useLocation } from "react-router-dom";
import { Fragment, PropsWithChildren, useRef } from "react";
+import { useBreakpoints } from "@jobber/hooks";
import AnimatedPresenceDisclosure from "./AnimatedPresenceDisclosure";
import styles from "./NavMenu.module.css";
+import { LeftDrawer } from "./LeftDrawer";
import { routes } from "../routes";
import { JobberLogo } from "../assets/JobberLogo.svg";
import { useAtlantisSite } from "../providers/AtlantisSiteProvider";
import { VisibleWhenFocused } from "../components/VisibleWhenFocused";
-interface NavMenuProps {
+export interface NavMenuProps {
readonly mainContentRef: React.RefObject;
}
@@ -17,7 +19,7 @@ interface NavMenuProps {
* @returns ReactNode
*/
export const NavMenu = ({ mainContentRef }: NavMenuProps) => {
- const { isMinimal } = useAtlantisSite();
+ const { isMinimal, isMobileMenuOpen, toggleMobileMenu } = useAtlantisSite();
const { pathname } = useLocation();
const selectedRef = useRef(null);
@@ -67,19 +69,20 @@ export const NavMenu = ({ mainContentRef }: NavMenuProps) => {
const skipToContent = () => {
mainContentRef.current?.focus();
+ toggleMobileMenu();
};
- return (
+ const menuContent = (