diff --git a/src/components/SideMenu/SideMenu.module.css b/src/components/SideMenu/SideMenu.module.css
new file mode 100644
index 0000000..bb0cb1d
--- /dev/null
+++ b/src/components/SideMenu/SideMenu.module.css
@@ -0,0 +1,88 @@
+.sideMenu {
+ height: 100vh;
+ width: 0;
+ position: fixed;
+ z-index: 20;
+ top: 0;
+ background: white;
+ overflow-x: hidden;
+ transition: 0.3s ease-out;
+}
+
+.sideMenuUnderlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ z-index: 20;
+ background: rgba(0, 0, 0, 0);
+ transition: background-color 0.3s;
+ pointer-events: none;
+}
+
+.sideMenuUnderlay.open {
+ background: rgba(0, 0, 0, 0.2);
+ pointer-events: auto;
+}
+
+.left.sideMenu {
+ left: 0;
+ box-shadow: 2px 0 5px rgba(0, 0, 0, 0.2);
+}
+
+.right.sideMenu {
+ right: 0;
+ box-shadow: -2px 0 5px rgba(0, 0, 0, 0.2);
+}
+
+.menuWrapper {
+ position: absolute;
+}
+
+.left.menuWrapper {
+ right: 0;
+}
+
+.right.menuWrapper {
+ left: 0;
+}
+
+.topNav {
+ width: 100%;
+ display: flex;
+}
+
+.right.sideMenu .closeButton {
+ margin: 10px;
+}
+
+.left.sideMenu .closeButton {
+ margin: 10px 10px 10px auto;
+}
+
+/* Landscape Phone */
+@media (min-width: 568px) and (max-width: 991px) and (orientation: landscape) {
+ .left.sideMenu.open,
+ .left.menuWrapper {
+ width: 80vh;
+ }
+
+ .right.sideMenu.open,
+ .right.menuWrapper {
+ width: 80vh;
+ }
+}
+
+/* Portrait Phone */
+@media (max-width: 567px) {
+ .left.sideMenu.open,
+ .left.menuWrapper {
+ width: 80vw;
+ }
+
+ .right.sideMenu.open,
+ .right.menuWrapper {
+ width: 80vw;
+ }
+}
diff --git a/src/components/SideMenu/SideMenu.tsx b/src/components/SideMenu/SideMenu.tsx
new file mode 100644
index 0000000..7b0802d
--- /dev/null
+++ b/src/components/SideMenu/SideMenu.tsx
@@ -0,0 +1,73 @@
+import type { ReactNode } from "react";
+import clsx from "clsx";
+import styles from "./SideMenu.module.css";
+
+interface SideMenuProps {
+ isOpen: boolean;
+ onClose: () => void;
+ children: ReactNode;
+}
+
+interface BaseMenuProps extends SideMenuProps {
+ position: "left" | "right";
+}
+
+function BaseMenu({ isOpen, onClose, children, position }: BaseMenuProps) {
+ const isLeftMenu = position === "left";
+
+ const handleUnderlayClick = (e: React.MouseEvent
) => {
+ if (e.target === e.currentTarget) {
+ onClose();
+ }
+ };
+
+ return (
+
+
e.stopPropagation()}
+ >
+
+
+
+
+ {children}
+
+
+
+ );
+}
+
+function newSideMenu(position: "left" | "right") {
+ return function SideMenu({ isOpen, onClose, children }: SideMenuProps) {
+ return (
+
+ {children}
+
+ );
+ };
+}
+
+const LeftMenu = newSideMenu("left");
+const RightMenu = newSideMenu("right");
+
+export { LeftMenu, RightMenu };
diff --git a/src/components/SideMenu/index.ts b/src/components/SideMenu/index.ts
new file mode 100644
index 0000000..2be693a
--- /dev/null
+++ b/src/components/SideMenu/index.ts
@@ -0,0 +1,2 @@
+import { LeftMenu, RightMenu } from "./SideMenu";
+export { LeftMenu, RightMenu };
diff --git a/src/main.tsx b/src/main.tsx
index 360a3c3..eff7ccc 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -1,11 +1,10 @@
-import { StrictMode } from 'react'
-import { createRoot } from 'react-dom/client'
-import './index.css'
-import App from './App.tsx'
+import { StrictMode } from "react";
+import { createRoot } from "react-dom/client";
+import "./index.css";
+import App from "./App.tsx";
-createRoot(document.getElementById('root')!).render(
+createRoot(document.getElementById("root")!).render(
,
-)
-
+);