Skip to content

Commit

Permalink
Merge pull request #21 from CodeVachon/mobile-menu
Browse files Browse the repository at this point in the history
Add Mobile Menu for better UX
  • Loading branch information
CodeVachon authored May 29, 2024
2 parents aab12d2 + 67b645e commit 9039a67
Show file tree
Hide file tree
Showing 4 changed files with 246 additions and 19 deletions.
122 changes: 121 additions & 1 deletion src/components/Icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,36 @@ interface IIconProps {
className?: string | ClassNames;
}

export const HamburgerIcon: React.FC<IIconProps> = ({ className = "size-6" }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className={new ClassNames(className).list()}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"
/>
</svg>
);

export const CloseIcon: React.FC<IIconProps> = ({ className = "size-6" }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className={new ClassNames(className).list()}
>
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18 18 6M6 6l12 12" />
</svg>
);

export const TwitterIcon: React.FC<IIconProps> = ({ className = "" }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
Expand Down Expand Up @@ -91,12 +121,102 @@ export const StarIcon: React.FC<IIconProps> = ({ className = "" }) => (
</svg>
);

export const LinkIcon: React.FC<IIconProps> = ({ className = "size-6" }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className={new ClassNames(className).list()}
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M13.19 8.688a4.5 4.5 0 0 1 1.242 7.244l-4.5 4.5a4.5 4.5 0 0 1-6.364-6.364l1.757-1.757m13.35-.622 1.757-1.757a4.5 4.5 0 0 0-6.364-6.364l-4.5 4.5a4.5 4.5 0 0 0 1.242 7.244"
/>
</svg>
);

export const HomeIcon: React.FC<IIconProps> = ({ className = "size-6" }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
className={new ClassNames(className).list()}
>
<path d="M19.006 3.705a.75.75 0 1 0-.512-1.41L6 6.838V3a.75.75 0 0 0-.75-.75h-1.5A.75.75 0 0 0 3 3v4.93l-1.006.365a.75.75 0 0 0 .512 1.41l16.5-6Z" />
<path
fillRule="evenodd"
d="M3.019 11.114 18 5.667v3.421l4.006 1.457a.75.75 0 1 1-.512 1.41l-.494-.18v8.475h.75a.75.75 0 0 1 0 1.5H2.25a.75.75 0 0 1 0-1.5H3v-9.129l.019-.007ZM18 20.25v-9.566l1.5.546v9.02H18Zm-9-6a.75.75 0 0 0-.75.75v4.5c0 .414.336.75.75.75h3a.75.75 0 0 0 .75-.75V15a.75.75 0 0 0-.75-.75H9Z"
clipRule="evenodd"
/>
</svg>
);

export const BlogIcon: React.FC<IIconProps> = ({ className = "size-6" }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
className={new ClassNames(className).list()}
>
<path
fillRule="evenodd"
d="M4.125 3C3.089 3 2.25 3.84 2.25 4.875V18a3 3 0 0 0 3 3h15a3 3 0 0 1-3-3V4.875C17.25 3.839 16.41 3 15.375 3H4.125ZM12 9.75a.75.75 0 0 0 0 1.5h1.5a.75.75 0 0 0 0-1.5H12Zm-.75-2.25a.75.75 0 0 1 .75-.75h1.5a.75.75 0 0 1 0 1.5H12a.75.75 0 0 1-.75-.75ZM6 12.75a.75.75 0 0 0 0 1.5h7.5a.75.75 0 0 0 0-1.5H6Zm-.75 3.75a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5H6a.75.75 0 0 1-.75-.75ZM6 6.75a.75.75 0 0 0-.75.75v3c0 .414.336.75.75.75h3a.75.75 0 0 0 .75-.75v-3A.75.75 0 0 0 9 6.75H6Z"
clipRule="evenodd"
/>
<path d="M18.75 6.75h1.875c.621 0 1.125.504 1.125 1.125V18a1.5 1.5 0 0 1-3 0V6.75Z" />
</svg>
);

export const UsesIcon: React.FC<IIconProps> = ({ className = "size-6" }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
className={new ClassNames(className).list()}
>
<path
fillRule="evenodd"
d="M12 6.75a5.25 5.25 0 0 1 6.775-5.025.75.75 0 0 1 .313 1.248l-3.32 3.319c.063.475.276.934.641 1.299.365.365.824.578 1.3.64l3.318-3.319a.75.75 0 0 1 1.248.313 5.25 5.25 0 0 1-5.472 6.756c-1.018-.086-1.87.1-2.309.634L7.344 21.3A3.298 3.298 0 1 1 2.7 16.657l8.684-7.151c.533-.44.72-1.291.634-2.309A5.342 5.342 0 0 1 12 6.75ZM4.117 19.125a.75.75 0 0 1 .75-.75h.008a.75.75 0 0 1 .75.75v.008a.75.75 0 0 1-.75.75h-.008a.75.75 0 0 1-.75-.75v-.008Z"
clipRule="evenodd"
/>
<path d="m10.076 8.64-2.201-2.2V4.874a.75.75 0 0 0-.364-.643l-3.75-2.25a.75.75 0 0 0-.916.113l-.75.75a.75.75 0 0 0-.113.916l2.25 3.75a.75.75 0 0 0 .643.364h1.564l2.062 2.062 1.575-1.297Z" />
<path
fillRule="evenodd"
d="m12.556 17.329 4.183 4.182a3.375 3.375 0 0 0 4.773-4.773l-3.306-3.305a6.803 6.803 0 0 1-1.53.043c-.394-.034-.682-.006-.867.042a.589.589 0 0 0-.167.063l-3.086 3.748Zm3.414-1.36a.75.75 0 0 1 1.06 0l1.875 1.876a.75.75 0 1 1-1.06 1.06L15.97 17.03a.75.75 0 0 1 0-1.06Z"
clipRule="evenodd"
/>
</svg>
);

export const CodeIcon: React.FC<IIconProps> = ({ className = "size-6" }) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
className={new ClassNames(className).list()}
>
<path
fillRule="evenodd"
d="M2.25 5.25a3 3 0 0 1 3-3h13.5a3 3 0 0 1 3 3V15a3 3 0 0 1-3 3h-3v.257c0 .597.237 1.17.659 1.591l.621.622a.75.75 0 0 1-.53 1.28h-9a.75.75 0 0 1-.53-1.28l.621-.622a2.25 2.25 0 0 0 .659-1.59V18h-3a3 3 0 0 1-3-3V5.25Zm1.5 0v7.5a1.5 1.5 0 0 0 1.5 1.5h13.5a1.5 1.5 0 0 0 1.5-1.5v-7.5a1.5 1.5 0 0 0-1.5-1.5H5.25a1.5 1.5 0 0 0-1.5 1.5Z"
clipRule="evenodd"
/>
</svg>
);

const icons = {
blueSky: BlueSkyIcon,
twitter: TwitterIcon,
github: GithubIcon,
fork: ForkIcon,
linkedin: LinkedInIcon
linkedin: LinkedInIcon,
link: LinkIcon,
home: HomeIcon,
blog: BlogIcon,
uses: UsesIcon,
code: CodeIcon
} as const;
export type IconName = keyof typeof icons;
export const Icon: React.FC<{ name: IconName } & IIconProps> = ({ name, className = "" }) => {
Expand Down
16 changes: 7 additions & 9 deletions src/components/ThemeToggle.astro
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const rgb = (hex: string) => {
</style>

<script is:inline>
const handleToggleClick = (e) => {
function handleToggleClick(e) {
if (e) {
e.preventDefault();
e.stopPropagation();
Expand All @@ -65,9 +65,9 @@ const rgb = (hex: string) => {

const isDark = element.classList.contains("dark");
localStorage.setItem("theme", isDark ? "dark" : "light");
};
}

const setThemeColor = (color) => {
function setThemeColor(color) {
if (!color) {
return;
}
Expand All @@ -81,7 +81,7 @@ const rgb = (hex: string) => {
document
.querySelector(".theme-button[data-value='" + color + "']")
.classList.add("selected");
};
}

function pageSetup() {
const theme = (() => {
Expand All @@ -107,15 +107,14 @@ const rgb = (hex: string) => {
setThemeColor(color);
}

const modeButton = document.getElementById("ThemeToggle");
if (modeButton) {
const modeButtons = document.querySelectorAll(".light-dark-toggle");
for (const modeButton of modeButtons) {
modeButton.addEventListener("click", handleToggleClick);
}

const themeButtons = document.querySelectorAll(".theme-button");
for (const themeButton of themeButtons) {
themeButton.addEventListener("click", (e) => {
console.log("themeButton click");
if (e) {
e.preventDefault();
e.stopPropagation();
Expand All @@ -136,8 +135,7 @@ const rgb = (hex: string) => {
<ul class="flex items-center gap-2">
<li class="flex items-center">
<button
id="ThemeToggle"
class="rounded-full bg-transparent p-1 transition duration-200 hover:bg-primary/50"
class="light-dark-toggle rounded-full bg-transparent p-1 transition duration-200 hover:bg-primary/50"
>
<svg class="size-8 lg:size-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path
Expand Down
99 changes: 90 additions & 9 deletions src/layouts/Layout.astro
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
import { Picture, getImage } from "astro:assets";
import { Icon } from "~components/Icons";
import { Icon, HamburgerIcon, CloseIcon } from "~components/Icons";
import type { IconName } from "~components/Icons";
import myImage from "../assets/me.png";
import Glow from "~components/Glow.astro";
Expand All @@ -22,11 +22,11 @@ interface Props {
publishDate?: string;
}
const navItems: Array<{ name: string; href: string; className?: string }> = [
{ name: "Home", href: "/", className: "hidden lg:block" },
{ name: "Blog", href: "/blog" },
{ name: "Uses", href: "/uses" },
{ name: "Code", href: "/code" }
const navItems: Array<{ name: string; href: string; className?: string; icon?: IconName }> = [
{ name: "Home", href: "/", className: "hidden lg:block", icon: "home" },
{ name: "Blog", href: "/blog", icon: "blog" },
{ name: "Uses", href: "/uses", icon: "uses" },
{ name: "Code", href: "/code", icon: "code" }
];
const socialMediaItems: Array<{ name: string; href: string; icon: IconName }> = [
Expand Down Expand Up @@ -135,6 +135,7 @@ import "./../styles/global.css";
<body
class="absolute inset-0 flex h-screen min-w-[350px] flex-col bg-white text-slate-900 lg:flex-row lg:overflow-hidden dark:bg-gray-950 dark:text-slate-50"
>
<input type="checkbox" id="menu-toggle" title="Toggle the Mobile Navigation" />
<section
id="sidebar"
class:list={[
Expand All @@ -150,7 +151,7 @@ import "./../styles/global.css";
>
<a
href="/"
class="flex w-1/3 min-w-[115px] items-center gap-4 decoration-transparent lg:w-full lg:flex-col lg:items-start"
class="flex w-full min-w-[115px] items-center gap-4 decoration-transparent sm:w-1/3 lg:w-full lg:flex-col lg:items-start"
>
<header class="flex items-center gap-4 lg:flex-col lg:items-start">
<Picture
Expand All @@ -166,7 +167,17 @@ import "./../styles/global.css";
<h1 class="text-lg font-bold lg:text-2xl">Christopher Vachon</h1>
</header>
</a>
<nav class="w-1/3 lg:w-full">
<nav>
<label
for="menu-toggle"
id="menu-open-toggle"
class="flex h-8 w-8 cursor-pointer items-center justify-center rounded transition-colors duration-200 ease-in-out hover:bg-primary md:hidden dark:bg-slate-800 dark:text-white"
aria-label="Toggle menu"
>
<HamburgerIcon className={"size-8"} />
</label>
</nav>
<nav class="hidden w-1/3 md:block lg:w-full">
<ul class="flex justify-center gap-6 lg:flex-col lg:gap-2">
{
navItems.map((item) => (
Expand All @@ -182,7 +193,7 @@ import "./../styles/global.css";
}
</ul>
</nav>
<nav class="flex w-1/3 justify-end lg:w-full lg:justify-start">
<nav class="hidden w-1/3 justify-end md:flex lg:w-full lg:justify-start">
<ol class="flex items-center gap-2">
{
socialMediaItems.map(({ name, href, icon }) => (
Expand Down Expand Up @@ -236,6 +247,68 @@ import "./../styles/global.css";
</p>
</footer>
</section>
<section id="flyout-menu">
<label id="backdrop" for="menu-toggle">
<label for="menu-toggle">
<CloseIcon
className={"size-12 text-white transition-colors duration-200 hover:text-primary dark:text-white"}
/>
</label>
</label>
<nav
class="fixed bottom-0 right-0 top-0 flex w-[90vw] min-w-[300px] flex-col justify-between overflow-y-auto bg-white shadow-md sm:w-[400px] dark:bg-slate-800"
>
<ol class="">
{
navItems.map((item) => (
<li class={Astro.url.pathname === item.href ? "bg-primary/50" : ""}>
<a
href={item.href}
title={`Check me out on ${item.name}`}
class="flex cursor-pointer items-center gap-6 p-4 decoration-transparent transition duration-200 ease-in-out hover:bg-primary hover:text-white"
>
<Icon name={item.icon ?? "link"} className="size-12" />
<span class="text-xl">{item.name}</span>
</a>
</li>
))
}
</ol>

<ol>
{
socialMediaItems.map(({ name, href, icon }) => (
<li>
<a
href={href}
target="_blank"
title={`Check me out on ${name}`}
class=" flex cursor-pointer items-center gap-6 p-4 decoration-transparent transition duration-200 ease-in-out hover:bg-primary hover:text-white"
>
<Icon name={icon} className="size-12" />
<span class="text-xl">{name}</span>
</a>
</li>
))
}

<li>
<label for="menu-toggle">
<div
class="flex cursor-pointer items-center gap-6 p-4 transition duration-200 ease-in-out hover:bg-primary hover:text-white"
>
<CloseIcon className={"size-12"} />
<span class="text-xl">Close</span>
</div>
</label>
</li>
<li class="p-4">
<ThemeToggle />
</li>
</ol>
</nav>
</section>

<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-RREP1WR95X"></script>
<script is:inline>
Expand All @@ -247,5 +320,13 @@ import "./../styles/global.css";

gtag("config", "G-RREP1WR95X");
</script>
<script is:inline>
document.addEventListener("astro:before-swap", (ev) => {
const toggle = document.getElementById("menu-toggle");
if (toggle.checked) {
toggle.checked = false;
}
});
</script>
</body>
</html>
28 changes: 28 additions & 0 deletions src/styles/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,32 @@
@apply absolute -left-36 top-4 size-32;
}
}

#flyout-menu {
#backdrop {
@apply fixed inset-0 z-0 bg-slate-500/0 backdrop-blur-none duration-300 ease-in-out;
label {
@apply absolute left-4 top-0 -translate-y-full transform cursor-pointer duration-500 ease-in-out;
}
}
nav {
@apply z-[1001] translate-x-full transform duration-500 ease-in-out;
}

#menu-toggle:checked ~ & {
#backdrop {
@apply fixed inset-0 z-[1000] bg-slate-500/30 backdrop-blur-sm;
label {
@apply sm:translate-y-4;
}
}
nav {
@apply translate-x-0;
}
}
}

#menu-toggle {
@apply hidden;
}
}

0 comments on commit 9039a67

Please sign in to comment.